summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp17
-rw-r--r--apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java5
-rw-r--r--api/current.txt54
-rw-r--r--api/removed.txt15
-rw-r--r--api/system-current.txt69
-rw-r--r--api/test-current.txt42
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java11
-rw-r--r--cmds/incident_helper/tests/KernelWakesParser_test.cpp12
-rw-r--r--cmds/statsd/src/atoms.proto16
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp40
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h7
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp127
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java10
-rw-r--r--config/hiddenapi-greylist.txt15
-rw-r--r--core/java/android/annotation/UnsupportedAppUsage.java24
-rw-r--r--core/java/android/app/Activity.java16
-rw-r--r--core/java/android/app/AppOpsManager.java24
-rw-r--r--core/java/android/app/ApplicationPackageManager.java9
-rw-r--r--core/java/android/app/UiAutomationConnection.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java69
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl50
-rw-r--r--core/java/android/app/role/IOnRoleHoldersChangedListener.aidl25
-rw-r--r--core/java/android/app/role/IRoleManager.aidl6
-rw-r--r--core/java/android/app/role/OnRoleHoldersChangedListener.java38
-rw-r--r--core/java/android/app/role/RoleManager.java157
-rw-r--r--core/java/android/app/role/RoleManagerCallback.java2
-rw-r--r--core/java/android/content/ContentResolver.java25
-rw-r--r--core/java/android/content/Intent.java2
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java27
-rw-r--r--core/java/android/content/pm/ICrossProfileApps.aidl2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java21
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java4
-rw-r--r--core/java/android/content/pm/PermissionInfo.java30
-rw-r--r--core/java/android/content/pm/SharedLibraryNames.java10
-rw-r--r--core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl2
-rw-r--r--core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java4
-rw-r--r--core/java/android/net/NetworkCapabilities.java2
-rw-r--r--core/java/android/os/Binder.java42
-rwxr-xr-x[-rw-r--r--]core/java/android/os/Build.java3
-rw-r--r--core/java/android/os/FileUtils.java54
-rw-r--r--core/java/android/os/INetworkManagementService.aidl6
-rw-r--r--core/java/android/os/PowerManagerInternal.java3
-rw-r--r--core/java/android/os/Temperature.java4
-rw-r--r--core/java/android/permission/IRuntimePermissionPresenter.aidl31
-rw-r--r--core/java/android/permission/RuntimePermissionPresentationInfo.aidl (renamed from core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl)2
-rw-r--r--core/java/android/permission/RuntimePermissionPresentationInfo.java110
-rw-r--r--core/java/android/permission/RuntimePermissionPresenter.java (renamed from core/java/android/content/pm/permission/RuntimePermissionPresenter.java)96
-rw-r--r--core/java/android/permission/RuntimePermissionPresenterService.java157
-rw-r--r--core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java10
-rw-r--r--core/java/android/provider/DocumentsContract.java93
-rw-r--r--core/java/android/provider/MediaStore.java77
-rw-r--r--core/java/android/provider/Settings.java47
-rw-r--r--core/java/android/service/autofill/AutofillFieldClassificationService.java123
-rw-r--r--core/java/android/service/autofill/FillResponse.java31
-rw-r--r--core/java/android/service/autofill/IAutofillFieldClassificationService.aidl7
-rw-r--r--core/java/android/service/autofill/UserData.java157
-rw-r--r--core/java/android/service/autofill/augmented/AugmentedAutofillService.java47
-rw-r--r--core/java/android/service/autofill/augmented/FillCallback.java10
-rw-r--r--core/java/android/service/autofill/augmented/FillRequest.java6
-rw-r--r--core/java/android/service/autofill/augmented/FillResponse.java2
-rw-r--r--core/java/android/service/notification/Adjustment.java17
-rw-r--r--core/java/android/util/FeatureFlagUtils.java7
-rw-r--r--core/java/android/view/InsetsController.java26
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java31
-rw-r--r--core/java/android/view/ThreadedRenderer.java29
-rw-r--r--core/java/android/view/ViewRootImpl.java59
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java2
-rw-r--r--core/java/android/view/textclassifier/ConversationActions.java47
-rw-r--r--core/java/android/view/textclassifier/SystemTextClassifier.java6
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java67
-rw-r--r--core/java/android/view/textclassifier/TextLanguage.java42
-rw-r--r--core/java/android/view/textclassifier/TextLinks.java167
-rw-r--r--core/java/android/view/textclassifier/TextSelection.java58
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java54
-rw-r--r--core/java/com/android/internal/os/AppIdToPackageMap.java79
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java47
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java25
-rw-r--r--core/java/com/android/server/SystemConfig.java28
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp181
-rw-r--r--core/jni/android_hardware_SoundTrigger.cpp3
-rw-r--r--core/jni/android_media_AudioSystem.cpp7
-rw-r--r--core/jni/android_media_AudioTrack.cpp21
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp20
-rw-r--r--core/proto/android/app/settings_enums.proto6
-rw-r--r--core/proto/android/service/usb.proto2
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto6
-rw-r--r--core/res/AndroidManifest.xml29
-rw-r--r--core/res/res/values-am/strings.xml2
-rw-r--r--core/res/res/values-de/strings.xml6
-rw-r--r--core/res/res/values-et/strings.xml22
-rw-r--r--core/res/res/values-fi/strings.xml2
-rw-r--r--core/res/res/values-hi/strings.xml2
-rw-r--r--core/res/res/values-km/strings.xml2
-rw-r--r--core/res/res/values-ky/strings.xml8
-rw-r--r--core/res/res/values-lv/strings.xml2
-rw-r--r--core/res/res/values-mn/strings.xml10
-rw-r--r--core/res/res/values-mr-watch/strings.xml2
-rw-r--r--core/res/res/values-mr/strings.xml232
-rw-r--r--core/res/res/values-my/strings.xml10
-rw-r--r--core/res/res/values-nb/strings.xml2
-rw-r--r--core/res/res/values-nl/strings.xml2
-rw-r--r--core/res/res/values-pa/strings.xml6
-rw-r--r--core/res/res/values-sl/strings.xml14
-rw-r--r--core/res/res/values-sw/strings.xml6
-rw-r--r--core/res/res/values-zh-rHK/strings.xml2
-rw-r--r--core/res/res/values/attrs_manifest.xml6
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/strings.xml9
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--core/tests/coretests/src/android/os/BinderWorkSourceService.java16
-rw-r--r--core/tests/coretests/src/android/os/BinderWorkSourceTest.java20
-rw-r--r--core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl4
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java61
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java47
-rw-r--r--core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java74
-rw-r--r--core/tests/coretests/src/android/security/keystore/recovery/TestData.java88
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java3
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java5
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java3
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java6
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java208
-rw-r--r--data/etc/platform.xml9
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--libs/androidfw/LocaleData.cpp14
-rw-r--r--libs/androidfw/LocaleDataTables.cpp1454
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp5
-rw-r--r--libs/input/Android.bp1
-rw-r--r--libs/input/PointerController.cpp136
-rw-r--r--libs/input/PointerController.h12
-rw-r--r--libs/input/SpriteController.cpp24
-rw-r--r--libs/input/SpriteController.h8
-rw-r--r--location/java/android/location/ILocationManager.aidl8
-rw-r--r--location/java/android/location/LocationManager.java20
-rw-r--r--location/java/com/android/internal/location/ILocationProvider.aidl21
-rw-r--r--location/java/com/android/internal/location/ILocationProviderManager.aidl (renamed from location/java/android/location/IGnssStatusProvider.aidl)21
-rw-r--r--location/java/com/android/internal/location/ProviderRequest.java13
-rw-r--r--location/lib/api/current.txt21
-rw-r--r--location/lib/java/com/android/location/provider/LocationProviderBase.java264
-rw-r--r--location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java10
-rw-r--r--media/java/android/media/AudioFormat.java32
-rw-r--r--media/java/android/media/AudioManager.java9
-rw-r--r--media/java/android/media/AudioSystem.java5
-rw-r--r--media/java/android/media/AudioTrack.java32
-rw-r--r--media/java/android/media/MediaCodec.java58
-rw-r--r--media/java/android/media/MediaCodecInfo.java11
-rw-r--r--media/java/android/media/MediaFormat.java22
-rw-r--r--media/jni/android_media_MediaExtractor.cpp16
-rw-r--r--media/jni/soundpool/SoundPool.cpp17
-rw-r--r--media/jni/soundpool/SoundPool.h26
-rw-r--r--packages/ExtServices/res/values/strings.xml1
-rw-r--r--packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java67
-rw-r--r--packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java29
-rw-r--r--packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java67
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/Assistant.java91
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java140
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java14
-rw-r--r--packages/ExtServices/tests/Android.mk3
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java91
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java94
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java98
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java162
-rw-r--r--packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java41
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java81
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java17
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusionEngine.java127
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java2
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml1
-rw-r--r--packages/PackageInstaller/res/layout/uninstall_content_view.xml47
-rw-r--r--packages/PackageInstaller/res/values-mr/strings.xml42
-rw-r--r--packages/PackageInstaller/res/values/strings.xml2
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java11
-rwxr-xr-xpackages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java12
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java64
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java2
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/Android.bp14
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml64
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml50
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml38
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java267
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java51
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java181
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java52
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java9
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java39
-rw-r--r--packages/Shell/res/values-mr/strings.xml2
-rw-r--r--packages/SystemUI/res/layout/bubble_expanded_view.xml30
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml34
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml2
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java20
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto16
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--proto/src/wifi.proto21
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java6
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java20
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java58
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java30
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java24
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java44
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java55
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java16
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java126
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java109
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java43
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java847
-rw-r--r--services/core/java/com/android/server/LooperStatsService.java4
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java12
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java34
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java46
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java93
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java6
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java9
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java66
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java5
-rw-r--r--services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java17
-rw-r--r--services/core/java/com/android/server/infra/AbstractRemoteService.java130
-rw-r--r--services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java15
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java7
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java149
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java465
-rw-r--r--services/core/java/com/android/server/location/GnssMeasurementsProvider.java17
-rw-r--r--services/core/java/com/android/server/location/GnssNavigationMessageProvider.java26
-rw-r--r--services/core/java/com/android/server/location/GnssStatusListenerHelper.java76
-rw-r--r--services/core/java/com/android/server/location/LocationProviderInterface.java78
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java146
-rw-r--r--services/core/java/com/android/server/location/MockProvider.java113
-rw-r--r--services/core/java/com/android/server/location/PassiveProvider.java48
-rw-r--r--services/core/java/com/android/server/location/RemoteListenerHelper.java82
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java99
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java127
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java45
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java65
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java107
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java75
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java117
-rw-r--r--services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java612
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java12
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java17
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java19
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java44
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java113
-rw-r--r--services/core/java/com/android/server/role/RoleUserState.java68
-rw-r--r--services/core/java/com/android/server/signedconfig/SignatureVerifier.java109
-rw-r--r--services/core/java/com/android/server/signedconfig/SignedConfig.java48
-rw-r--r--services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java113
-rw-r--r--services/core/java/com/android/server/signedconfig/SignedConfigService.java3
-rw-r--r--services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java3
-rw-r--r--services/core/java/com/android/server/wm/BarController.java2
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java33
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java17
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp90
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java85
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java130
-rw-r--r--services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java5
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java29
-rw-r--r--services/tests/servicestests/Android.bp2
-rw-r--r--services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java151
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java111
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java560
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java143
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java75
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java3
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java31
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java49
-rw-r--r--telecomm/java/android/telecom/Call.java11
-rw-r--r--telecomm/java/android/telecom/PhoneAccountSuggestion.java135
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java10
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java235
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java29
-rw-r--r--telephony/java/android/telephony/ServiceState.java84
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java124
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java74
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java3
-rw-r--r--telephony/java/android/telephony/emergency/EmergencyNumber.java133
-rw-r--r--telephony/java/android/telephony/ims/ImsReasonInfo.java466
-rw-r--r--telephony/java/android/telephony/ims/ImsSsData.java79
-rw-r--r--telephony/java/android/telephony/ims/ImsSsInfo.java12
-rw-r--r--telephony/java/com/android/internal/telephony/CallerInfo.java19
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl2
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl4
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java1
-rw-r--r--tests/net/java/android/net/NetworkCapabilitiesTest.java11
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp4
-rwxr-xr-xtools/hiddenapi/exclude.sh1
-rwxr-xr-xtools/localedata/extract_icu_data.py2
-rw-r--r--tools/processors/view_inspector/Android.bp27
-rw-r--r--tools/processors/view_inspector/OWNERS3
-rw-r--r--tools/processors/view_inspector/TEST_MAPPING7
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java117
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java200
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java76
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java406
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java32
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java161
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java78
-rw-r--r--tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor1
-rw-r--r--tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java130
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt31
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt39
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt37
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt109
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt39
-rw-r--r--tools/signedconfig/debug_key.pem5
-rw-r--r--tools/signedconfig/debug_public.pem4
-rwxr-xr-xtools/signedconfig/debug_sign.sh6
-rwxr-xr-xtools/signedconfig/gen_priv_key.sh7
-rwxr-xr-xtools/signedconfig/verify_b64.sh10
-rw-r--r--tools/stats_log_api_gen/main.cpp47
344 files changed, 13493 insertions, 4919 deletions
diff --git a/Android.bp b/Android.bp
index 55686734e8ea..32bd40861839 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,7 @@ java_defaults {
"core/java/android/app/backup/IRestoreObserver.aidl",
"core/java/android/app/backup/IRestoreSession.aidl",
"core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+ "core/java/android/app/role/IOnRoleHoldersChangedListener.aidl",
"core/java/android/app/role/IRoleManager.aidl",
"core/java/android/app/role/IRoleManagerCallback.aidl",
"core/java/android/app/slice/ISliceManager.aidl",
@@ -265,6 +266,7 @@ java_defaults {
"core/java/android/os/storage/IStorageEventListener.aidl",
"core/java/android/os/storage/IStorageShutdownObserver.aidl",
"core/java/android/os/storage/IObbActionListener.aidl",
+ "core/java/android/permission/IRuntimePermissionPresenter.aidl",
"core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
":keystore_aidl",
"core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
@@ -442,7 +444,6 @@ java_defaults {
"location/java/android/location/IGeocodeProvider.aidl",
"location/java/android/location/IGeofenceProvider.aidl",
"location/java/android/location/IGnssStatusListener.aidl",
- "location/java/android/location/IGnssStatusProvider.aidl",
"location/java/android/location/IGnssMeasurementsListener.aidl",
"location/java/android/location/IGnssNavigationMessageListener.aidl",
"location/java/android/location/ILocationListener.aidl",
@@ -451,6 +452,7 @@ java_defaults {
"location/java/android/location/IGpsGeofenceHardware.aidl",
"location/java/android/location/INetInitiatedListener.aidl",
"location/java/com/android/internal/location/ILocationProvider.aidl",
+ "location/java/com/android/internal/location/ILocationProviderManager.aidl",
"media/java/android/media/IAudioFocusDispatcher.aidl",
"media/java/android/media/IAudioRoutesObserver.aidl",
"media/java/android/media/IAudioService.aidl",
@@ -689,7 +691,6 @@ java_defaults {
"frameworks/av/media/libaudioclient/aidl",
"frameworks/native/aidl/gui",
"system/core/storaged/binder",
- "system/netd/server/binder",
"system/vold/binder",
"system/bt/binder",
"system/security/keystore/binder",
@@ -783,18 +784,6 @@ java_library {
],
}
-// A host library containing the inspector annotations for inspector-annotation-processor.
-java_library_host {
- name: "inspector-annotation",
- srcs: [
- "core/java/android/view/inspector/InspectableNodeName.java",
- "core/java/android/view/inspector/InspectableProperty.java",
- // Needed for the ResourceId.ID_NULL constant
- "core/java/android/content/res/ResourceId.java",
- "core/java/android/annotation/AnyRes.java",
- ],
-}
-
// A host library including just UnsupportedAppUsage.java so that the annotation
// processor can also use this annotation.
java_library_host {
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 5be0cb025105..99e4ba1bb804 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -39,6 +39,7 @@ import org.junit.runner.RunWith;
@LargeTest
public class BinderCallsStatsPerfTest {
private static final int DEFAULT_BUCKET_SIZE = 1000;
+ private static final int WORKSOURCE_UID = 1;
static class FakeCpuTimeBinderCallsStats extends BinderCallsStats {
private int mTimeMs;
@@ -117,8 +118,8 @@ public class BinderCallsStatsPerfTest {
Binder b = new Binder();
while (state.keepRunning()) {
for (int i = 0; i < 10000; i++) {
- CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize);
- mBinderCallsStats.callEnded(s, 0, 0);
+ CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize, WORKSOURCE_UID);
+ mBinderCallsStats.callEnded(s, 0, 0, WORKSOURCE_UID);
}
}
}
diff --git a/api/current.txt b/api/current.txt
index 81993dfc569d..4804136173f1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9402,6 +9402,7 @@ package android.content {
method public static void cancelSync(android.content.SyncRequest);
method public final android.net.Uri canonicalize(android.net.Uri);
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+ method public android.os.Bundle getCache(android.net.Uri);
method public static deprecated android.content.SyncInfo getCurrentSync();
method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
method public static int getIsSyncable(android.accounts.Account, java.lang.String);
@@ -9432,6 +9433,7 @@ package android.content {
method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public void putCache(android.net.Uri, android.os.Bundle);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -10236,6 +10238,7 @@ package android.content {
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final java.lang.String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -22742,8 +22745,8 @@ package android.location {
method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
- method public void clearTestProviderEnabled(java.lang.String);
- method public void clearTestProviderLocation(java.lang.String);
+ method public deprecated void clearTestProviderEnabled(java.lang.String);
+ method public deprecated void clearTestProviderLocation(java.lang.String);
method public deprecated void clearTestProviderStatus(java.lang.String);
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
@@ -22955,6 +22958,7 @@ package android.media {
method public int getChannelIndexMask();
method public int getChannelMask();
method public int getEncoding();
+ method public int getFrameSizeInBytes();
method public int getSampleRate();
method public void writeToParcel(android.os.Parcel, int);
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -23057,6 +23061,7 @@ package android.media {
method public deprecated boolean isBluetoothA2dpOn();
method public boolean isBluetoothScoAvailableOffCall();
method public boolean isBluetoothScoOn();
+ method public static boolean isHapticPlaybackSupported();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public static boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
@@ -23394,6 +23399,7 @@ package android.media {
method public int getStreamType();
method public boolean getTimestamp(android.media.AudioTimestamp);
method public int getUnderrunCount();
+ method public static boolean isDirectPlaybackSupported(android.media.AudioFormat, android.media.AudioAttributes);
method public void pause() throws java.lang.IllegalStateException;
method public void play() throws java.lang.IllegalStateException;
method public void registerStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -23974,6 +23980,7 @@ package android.media {
field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
+ field public static final java.lang.String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
field public static final java.lang.String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
field public static final java.lang.String PARAMETER_KEY_SUSPEND = "drop-input-frames";
field public static final java.lang.String PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
@@ -24247,6 +24254,7 @@ package android.media {
field public static final int HEVCProfileMain = 1; // 0x1
field public static final int HEVCProfileMain10 = 2; // 0x2
field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
+ field public static final int HEVCProfileMain10HDR10Plus = 8192; // 0x2000
field public static final int HEVCProfileMainStill = 4; // 0x4
field public static final int MPEG2LevelH14 = 2; // 0x2
field public static final int MPEG2LevelHL = 3; // 0x3
@@ -24308,8 +24316,10 @@ package android.media {
field public static final int VP9Profile1 = 2; // 0x2
field public static final int VP9Profile2 = 4; // 0x4
field public static final int VP9Profile2HDR = 4096; // 0x1000
+ field public static final int VP9Profile2HDR10Plus = 16384; // 0x4000
field public static final int VP9Profile3 = 8; // 0x8
field public static final int VP9Profile3HDR = 8192; // 0x2000
+ field public static final int VP9Profile3HDR10Plus = 32768; // 0x8000
field public int level;
field public int profile;
}
@@ -24708,6 +24718,7 @@ package android.media {
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
field public static final java.lang.String KEY_GRID_COLUMNS = "grid-cols";
field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
+ field public static final java.lang.String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -24746,6 +24757,7 @@ package android.media {
field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+ field public static final java.lang.String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
field public static final java.lang.String MIMETYPE_AUDIO_FLAC = "audio/flac";
field public static final java.lang.String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
field public static final java.lang.String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
@@ -37444,6 +37456,8 @@ package android.provider {
field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT";
field public static final java.lang.String METADATA_EXIF = "android:documentExif";
+ field public static final java.lang.String METADATA_TREE_COUNT = "android:metadataTreeCount";
+ field public static final java.lang.String METADATA_TREE_SIZE = "android:metadataTreeSize";
field public static final java.lang.String METADATA_TYPES = "android:documentMetadataTypes";
field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
field public static final java.lang.String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
@@ -37820,6 +37834,7 @@ package android.provider {
public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns {
method public static android.net.Uri getContentUri(java.lang.String);
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/download";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
field public static final android.net.Uri INTERNAL_CONTENT_URI;
}
@@ -40463,6 +40478,7 @@ package android.service.autofill {
method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
+ method public android.service.autofill.FillResponse.Builder setUserData(android.service.autofill.UserData);
}
public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -40558,6 +40574,7 @@ package android.service.autofill {
public final class UserData implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getFieldClassificationAlgorithm();
+ method public java.lang.String getFieldClassificationAlgorithmForCategory(java.lang.String);
method public java.lang.String getId();
method public static int getMaxCategoryCount();
method public static int getMaxFieldClassificationIdsSize();
@@ -40573,6 +40590,7 @@ package android.service.autofill {
method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
method public android.service.autofill.UserData build();
method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithm(java.lang.String, android.os.Bundle);
+ method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithmForCategory(java.lang.String, java.lang.String, android.os.Bundle);
}
public abstract interface Validator {
@@ -42322,8 +42340,9 @@ package android.telecom {
method public void swapConference();
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
- field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ field public static final deprecated java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
+ field public static final java.lang.String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
@@ -42894,6 +42913,20 @@ package android.telecom {
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
}
+ public final class PhoneAccountSuggestion implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+ method public int getReason();
+ method public boolean shouldAutoSelect();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountSuggestion> CREATOR;
+ field public static final int REASON_FREQUENT = 2; // 0x2
+ field public static final int REASON_INTRA_CARRIER = 1; // 0x1
+ field public static final int REASON_NONE = 0; // 0x0
+ field public static final int REASON_OTHER = 4; // 0x4
+ field public static final int REASON_USER_SET = 3; // 0x3
+ }
+
public final class RemoteConference {
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
@@ -43781,10 +43814,10 @@ package android.telephony {
method public static java.lang.String getStrippedReversed(java.lang.String);
method public static final boolean is12Key(char);
method public static final boolean isDialable(char);
- method public static boolean isEmergencyNumber(java.lang.String);
+ method public static deprecated boolean isEmergencyNumber(java.lang.String);
method public static boolean isGlobalPhoneNumber(java.lang.String);
method public static boolean isISODigit(char);
- method public static boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
+ method public static deprecated boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
method public static final boolean isNonSeparator(char);
method public static final boolean isReallyDialable(char);
method public static final boolean isStartsPostDial(char);
@@ -44075,12 +44108,14 @@ package android.telephony {
method public static int getSlotIndex(int);
method public int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+ method public java.util.List<android.telephony.SubscriptionInfo> getSubscriptionsInGroup(int);
method public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
method public static boolean isUsableSubscriptionId(int);
method public static boolean isValidSubscriptionId(int);
method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+ method public boolean removeSubscriptionsFromGroup(int[]);
method public java.lang.String setSubscriptionGroup(int[]);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -44492,11 +44527,13 @@ package android.telephony.emergency {
method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
method public int getEmergencyServiceCategoryBitmask();
+ method public java.lang.String getMnc();
method public java.lang.String getNumber();
method public boolean isFromSources(int);
method public boolean isInEmergencyServiceCategories(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+ field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
@@ -52480,6 +52517,7 @@ package android.view.textclassifier {
public static final class ConversationActions.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
method public java.util.List<java.lang.String> getHints();
method public int getMaxSuggestions();
@@ -52593,6 +52631,7 @@ package android.view.textclassifier {
public static final class TextClassification.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
method public android.os.Bundle getExtras();
@@ -52710,6 +52749,7 @@ package android.view.textclassifier {
public static final class TextLanguage.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.Bundle getExtras();
method public java.lang.CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
@@ -52741,6 +52781,7 @@ package android.view.textclassifier {
public static final class TextLinks.Builder {
ctor public TextLinks.Builder(java.lang.String);
method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
+ method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>, android.os.Bundle);
method public android.view.textclassifier.TextLinks build();
method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
method public android.view.textclassifier.TextLinks.Builder setExtras(android.os.Bundle);
@@ -52748,6 +52789,7 @@ package android.view.textclassifier {
public static final class TextLinks.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
method public android.os.Bundle getExtras();
@@ -52770,6 +52812,7 @@ package android.view.textclassifier {
method public int getEnd();
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.os.Bundle getExtras();
method public int getStart();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
@@ -52804,6 +52847,7 @@ package android.view.textclassifier {
public static final class TextSelection.Request implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.String getCallingPackageName();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
method public android.os.Bundle getExtras();
diff --git a/api/removed.txt b/api/removed.txt
index f7106d2207ec..e3e8b6397e6f 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -508,6 +508,21 @@ package android.provider {
field public static final deprecated java.lang.String TIMESTAMP = "timestamp";
}
+ public final class DocumentsContract {
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
+ method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ }
+
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index d488b864d1f0..6db4ec75632d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7,6 +7,7 @@ package android {
field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field public static final deprecated java.lang.String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
+ field public static final java.lang.String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
field public static final java.lang.String ACCESS_MTP = "android.permission.ACCESS_MTP";
field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
@@ -90,6 +91,7 @@ package android {
field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
+ field public static final java.lang.String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
field public static final java.lang.String INTERACT_ACROSS_USERS_FULL = "android.permission.INTERACT_ACROSS_USERS_FULL";
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
@@ -134,6 +136,7 @@ package android {
field public static final java.lang.String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final java.lang.String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final java.lang.String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
+ field public static final java.lang.String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
field public static final java.lang.String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
@@ -171,6 +174,7 @@ package android {
field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
+ field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -315,7 +319,6 @@ package android.app {
method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
method public static java.lang.String[] getOpStrs();
method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
- method public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOpStrs(java.lang.String[]);
method public static int opToDefaultMode(java.lang.String);
method public static java.lang.String opToPermission(java.lang.String);
method public void setMode(java.lang.String, int, java.lang.String, int);
@@ -598,6 +601,8 @@ package android.app.admin {
method public boolean isDeviceManaged();
method public boolean isDeviceProvisioned();
method public boolean isDeviceProvisioningConfigApplied();
+ method public boolean isManagedKiosk();
+ method public boolean isUnattendedManagedKiosk();
method public void notifyPendingSystemUpdate(long);
method public void notifyPendingSystemUpdate(long, boolean);
method public boolean packageHasActiveAdmins(java.lang.String);
@@ -884,12 +889,18 @@ package android.app.job {
package android.app.role {
+ public abstract interface OnRoleHoldersChangedListener {
+ method public abstract void onRoleHoldersChanged(java.lang.String, android.os.UserHandle);
+ }
+
public final class RoleManager {
+ method public void addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean addRoleHolderFromController(java.lang.String, java.lang.String);
method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+ method public void removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
method public void setRoleNamesFromController(java.util.List<java.lang.String>);
@@ -1043,6 +1054,10 @@ package android.content {
method public void setDetectNotResponding(long);
}
+ public abstract class ContentResolver {
+ method public android.graphics.drawable.Drawable getTypeDrawable(java.lang.String);
+ }
+
public abstract class Context {
method public boolean bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
method public abstract android.content.Context createCredentialProtectedStorageContext();
@@ -1141,6 +1156,10 @@ package android.content.pm {
field public int targetSandboxVersion;
}
+ public class CrossProfileApps {
+ method public void startAnyActivity(android.content.ComponentName, android.os.UserHandle);
+ }
+
public final class InstantAppInfo implements android.os.Parcelable {
ctor public InstantAppInfo(android.content.pm.ApplicationInfo, java.lang.String[], java.lang.String[]);
ctor public InstantAppInfo(java.lang.String, java.lang.CharSequence, java.lang.String[], java.lang.String[]);
@@ -1253,6 +1272,7 @@ package android.content.pm {
method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public java.lang.String getWellbeingPackageName();
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1353,8 +1373,10 @@ package android.content.pm {
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
field public static final int FLAG_REMOVED = 2; // 0x2
+ field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
field public java.lang.String backgroundPermission;
field public int requestRes;
}
@@ -1399,7 +1421,7 @@ package android.content.pm.dex {
package android.content.pm.permission {
- public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
+ public final deprecated class RuntimePermissionPresentationInfo implements android.os.Parcelable {
ctor public RuntimePermissionPresentationInfo(java.lang.CharSequence, boolean, boolean);
method public int describeContents();
method public java.lang.CharSequence getLabel();
@@ -4412,11 +4434,31 @@ package android.permission {
method public int getTargetSdk();
}
+ public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
+ ctor public RuntimePermissionPresentationInfo(java.lang.CharSequence, boolean, boolean);
+ method public int describeContents();
+ method public java.lang.CharSequence getLabel();
+ method public boolean isGranted();
+ method public boolean isStandard();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
+ }
+
+ public abstract class RuntimePermissionPresenterService extends android.app.Service {
+ ctor public RuntimePermissionPresenterService();
+ method public final void attachBaseContext(android.content.Context);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
+ method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+ method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.permission.RuntimePermissionPresenterService";
+ }
+
}
package android.permissionpresenterservice {
- public abstract class RuntimePermissionPresenterService extends android.app.Service {
+ public abstract deprecated class RuntimePermissionPresenterService extends android.app.Service {
ctor public RuntimePermissionPresenterService();
method public final void attachBaseContext(android.content.Context);
method public final android.os.IBinder onBind(android.content.Intent);
@@ -4542,11 +4584,6 @@ package android.provider {
field public static final int FLAG_REMOVABLE_USB = 524288; // 0x80000
}
- public final class MediaStore {
- method public static void deleteContributedMedia(android.content.Context, java.lang.String);
- method public static long getContributedMediaSize(android.content.Context, java.lang.String);
- }
-
public abstract class SearchIndexableData {
ctor public SearchIndexableData();
ctor public SearchIndexableData(android.content.Context);
@@ -4891,7 +4928,10 @@ package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
method public android.os.IBinder onBind(android.content.Intent);
- method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ method public float[][] onCalculateScores(java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>, java.util.List<java.lang.String>, java.lang.String, android.os.Bundle, java.util.Map, java.util.Map);
+ method public deprecated float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ field public static final java.lang.String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
+ field public static final java.lang.String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
@@ -5609,6 +5649,10 @@ package android.telecom {
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
}
+ public final class PhoneAccountSuggestion implements android.os.Parcelable {
+ ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
+ }
+
public final class RemoteConference {
method public deprecated void setAudioState(android.telecom.AudioState);
}
@@ -5949,6 +5993,7 @@ package android.telephony {
method public int getVoiceActivationState();
method public boolean handlePinMmi(java.lang.String);
method public boolean handlePinMmiForSubscriber(int, java.lang.String);
+ method public boolean isCurrentPotentialEmergencyNumber(java.lang.String);
method public boolean isDataConnectivityPossible();
method public deprecated boolean isIdle();
method public deprecated boolean isOffhook();
@@ -6810,12 +6855,6 @@ package android.telephony.ims {
method public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
}
- public static abstract class ImsSsInfo.ClirInterrogationStatus implements java.lang.annotation.Annotation {
- }
-
- public static abstract class ImsSsInfo.ClirOutgoingState implements java.lang.annotation.Annotation {
- }
-
public final class ImsStreamMediaProfile implements android.os.Parcelable {
ctor public ImsStreamMediaProfile(int, int, int, int, int);
method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
diff --git a/api/test-current.txt b/api/test-current.txt
index 91e8ec62e781..4717969902b9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10,6 +10,8 @@ package android {
field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
+ field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final java.lang.String WRITE_OBB = "android.permission.WRITE_OBB";
}
}
@@ -276,6 +278,23 @@ package android.app.backup {
}
+package android.app.role {
+
+ public final class RoleManager {
+ method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+ method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+ method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
+ method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+ method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+ }
+
+ public abstract interface RoleManagerCallback {
+ method public abstract void onFailure();
+ method public abstract void onSuccess();
+ }
+
+}
+
package android.app.usage {
public class NetworkStatsManager {
@@ -355,8 +374,10 @@ package android.content.pm {
}
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+ field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
field public java.lang.String backgroundPermission;
}
@@ -988,8 +1009,8 @@ package android.provider {
}
public final class MediaStore {
- method public static void deleteContributedMedia(android.content.Context, java.lang.String);
- method public static long getContributedMediaSize(android.content.Context, java.lang.String);
+ method public static void deleteContributedMedia(android.content.Context, java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ method public static long getContributedMediaSize(android.content.Context, java.lang.String, android.os.UserHandle) throws java.io.IOException;
}
public final class Settings {
@@ -1075,6 +1096,15 @@ package android.security.keystore {
package android.service.autofill {
+ public abstract class AutofillFieldClassificationService extends android.app.Service {
+ method public android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
+ field public static final java.lang.String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
+ field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+ field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
+ }
+
public final class CharSequenceTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
}
@@ -1131,6 +1161,10 @@ package android.service.autofill {
method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
}
+ public final class UserData implements android.os.Parcelable {
+ method public android.util.ArrayMap<java.lang.String, java.lang.String> getFieldClassificationAlgorithms();
+ }
+
public abstract interface ValueFinder {
method public default java.lang.String findByAutofillId(android.view.autofill.AutofillId);
method public abstract android.view.autofill.AutofillValue findRawValueByAutofillId(android.view.autofill.AutofillId);
@@ -1249,6 +1283,10 @@ package android.telecom {
ctor public CallAudioState(boolean, int, int, android.bluetooth.BluetoothDevice, java.util.Collection<android.bluetooth.BluetoothDevice>);
}
+ public final class PhoneAccountSuggestion implements android.os.Parcelable {
+ ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
+ }
+
}
package android.telephony {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3723fce9d4f0..e3748f1653ab 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -201,7 +201,7 @@ public class Bmgr {
private void doEnabled(@UserIdInt int userId) {
try {
- boolean isEnabled = mBmgr.isBackupEnabled();
+ boolean isEnabled = mBmgr.isBackupEnabledForUser(userId);
System.out.println("Backup Manager currently "
+ enableToString(isEnabled));
} catch (RemoteException e) {
@@ -219,7 +219,7 @@ public class Bmgr {
try {
boolean enable = Boolean.parseBoolean(arg);
- mBmgr.setBackupEnabled(enable);
+ mBmgr.setBackupEnabledForUser(userId, enable);
System.out.println("Backup Manager now " + enableToString(enable));
} catch (NumberFormatException e) {
showUsage();
@@ -232,7 +232,7 @@ public class Bmgr {
void doRun(@UserIdInt int userId) {
try {
- mBmgr.backupNow();
+ mBmgr.backupNowForUser(userId);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -416,7 +416,8 @@ public class Bmgr {
(monitorState != Monitor.OFF)
? new BackupMonitor(monitorState == Monitor.VERBOSE)
: null;
- int err = mBmgr.requestBackup(
+ int err = mBmgr.requestBackupForUser(
+ userId,
packages.toArray(new String[packages.size()]),
observer,
monitor,
@@ -477,7 +478,7 @@ public class Bmgr {
String arg = nextArg();
if ("backups".equals(arg)) {
try {
- mBmgr.cancelBackups();
+ mBmgr.cancelBackupsForUser(userId);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
index f92d81361eab..573ca4f632e0 100644
--- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -84,9 +84,9 @@ TEST_F(KernelWakesParserTest, Normal) {
record1->set_event_count(8);
record1->set_wakeup_count(0);
record1->set_expire_count(0);
- record1->set_active_since(0l);
- record1->set_total_time(0l);
- record1->set_max_time(0l);
+ record1->set_active_since(0L);
+ record1->set_total_time(0L);
+ record1->set_max_time(0L);
record1->set_last_change(131348LL);
record1->set_prevent_suspend_time(0LL);
@@ -96,9 +96,9 @@ TEST_F(KernelWakesParserTest, Normal) {
record2->set_event_count(143);
record2->set_wakeup_count(0);
record2->set_expire_count(0);
- record2->set_active_since(0l);
- record2->set_total_time(123l);
- record2->set_max_time(3l);
+ record2->set_active_since(0L);
+ record2->set_total_time(123L);
+ record2->set_max_time(3L);
record2->set_last_change(2067286206LL);
record2->set_prevent_suspend_time(0LL);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 78d8e29b25d9..f58caff76d27 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1217,6 +1217,12 @@ message UsbDeviceAttached {
optional bool has_audio = 3;
optional bool has_hid = 4;
optional bool has_storage = 5;
+ enum State {
+ STATE_DISCONNECTED = 0;
+ STATE_CONNECTED = 1;
+ }
+ optional State state = 6;
+ optional int64 last_connect_duration_ms = 7;
}
@@ -1265,14 +1271,18 @@ message BluetoothConnectionStateChanged {
* Logs when something is plugged into or removed from the USB-C connector.
*
* Logged from:
- * Vendor USB HAL.
+ * UsbService
*/
message UsbConnectorStateChanged {
enum State {
- DISCONNECTED = 0;
- CONNECTED = 1;
+ STATE_DISCONNECTED = 0;
+ STATE_CONNECTED = 1;
}
optional State state = 1;
+ optional string id = 2;
+ // Last active session in ms.
+ // 0 when the port is in connected state.
+ optional int64 last_connect_duration_millis = 3;
}
/**
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index f25520be4749..1f22a6af3a3d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -432,6 +432,26 @@ bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
return false;
}
+bool ValueMetricProducer::hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey) {
+ // ===========GuardRail==============
+ // 1. Report the tuple count if the tuple count > soft limit
+ if (mCurrentFullBucket.find(newKey) != mCurrentFullBucket.end()) {
+ return false;
+ }
+ if (mCurrentFullBucket.size() > mDimensionSoftLimit - 1) {
+ size_t newTupleCount = mCurrentFullBucket.size() + 1;
+ // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+ if (newTupleCount > mDimensionHardLimit) {
+ ALOGE("ValueMetric %lld dropping data for full bucket dimension key %s",
+ (long long)mMetricId,
+ newKey.toString().c_str());
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) {
for (const FieldValue& value : event.getValues()) {
if (value.mField.matches(matcher)) {
@@ -496,6 +516,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
return;
}
+ interval.seenNewData = true;
if (mUseDiff) {
if (!interval.hasBase) {
@@ -648,6 +669,9 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) {
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
+ if (hitFullBucketGuardRailLocked(slice.first)) {
+ continue;
+ }
// TODO: fix this when anomaly can accept double values
mCurrentFullBucket[slice.first] += slice.second[0].value.long_value;
}
@@ -679,11 +703,21 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) {
}
}
- // Reset counters
- for (auto& slice : mCurrentSlicedBucket) {
- for (auto& interval : slice.second) {
+ for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
+ bool obsolete = true;
+ for (auto& interval : it->second) {
interval.hasValue = false;
interval.sampleSize = 0;
+ if (interval.seenNewData) {
+ obsolete = false;
+ }
+ interval.seenNewData = false;
+ }
+
+ if (obsolete) {
+ it = mCurrentSlicedBucket.erase(it);
+ } else {
+ it++;
}
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 36ae2146f465..4991af4cc75c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -128,7 +128,9 @@ private:
int sampleSize;
// If this dimension has any non-tainted value. If not, don't report the
// dimension.
- bool hasValue;
+ bool hasValue = false;
+ // Whether new data is seen in the bucket.
+ bool seenNewData = false;
} Interval;
std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
@@ -146,6 +148,8 @@ private:
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+ bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
+
void pullAndMatchEventsLocked(const int64_t timestampNs);
// Reset diff base and mHasGlobalBase
@@ -202,6 +206,7 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+ FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1bd34f52a64e..55245033e762 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1469,7 +1469,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
}
/*
- * Tests pulled atoms with no conditions
+ * Tests zero default base.
*/
TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
ValueMetric metric;
@@ -1554,7 +1554,7 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
}
/*
- * Tests pulled atoms with no conditions
+ * Tests using zero default base with failed pull.
*/
TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
ValueMetric metric;
@@ -1681,6 +1681,129 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
}
+/*
+ * Tests trim unused dimension key if no new data is seen in an entire bucket.
+ */
+TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+ event->write(1);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ auto iter = valueProducer.mCurrentSlicedBucket.begin();
+ auto& interval1 = iter->second[0];
+ EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(3, interval1.base.long_value);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ vector<shared_ptr<LogEvent>> allData;
+
+ allData.clear();
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event1->write(2);
+ event1->write(4);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event2->write(1);
+ event2->write(11);
+ event2->init();
+ allData.push_back(event1);
+ allData.push_back(event2);
+
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval1.hasBase);
+ EXPECT_EQ(11, interval1.base.long_value);
+ EXPECT_EQ(true, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
+ EXPECT_TRUE(interval1.seenNewData);
+
+ auto it = valueProducer.mCurrentSlicedBucket.begin();
+ for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+ if (it != iter) {
+ break;
+ }
+ }
+ EXPECT_TRUE(it != iter);
+ auto& interval2 = it->second[0];
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ EXPECT_TRUE(interval2.seenNewData);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ // next pull somehow did not happen, skip to end of bucket 3
+ allData.clear();
+ event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+ event1->write(2);
+ event1->write(5);
+ event1->init();
+ allData.push_back(event1);
+ valueProducer.onDataPulled(allData);
+
+ EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+
+ EXPECT_EQ(false, interval1.hasBase);
+ EXPECT_EQ(false, interval1.hasValue);
+ EXPECT_EQ(8, interval1.value.long_value);
+ // on probation now
+ EXPECT_FALSE(interval1.seenNewData);
+
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(5, interval2.base.long_value);
+ EXPECT_EQ(false, interval2.hasValue);
+ // back to good status
+ EXPECT_TRUE(interval2.seenNewData);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+ event1->write(2);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+ valueProducer.onDataPulled(allData);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, interval2.hasBase);
+ EXPECT_EQ(13, interval2.base.long_value);
+ EXPECT_EQ(true, interval2.hasValue);
+ EXPECT_EQ(8, interval2.value.long_value);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index f3bf6e7b7231..a39f5e30bd51 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,7 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
+ private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
"add-or-remove-call-companion-app";
@@ -68,6 +69,7 @@ public final class Telecom extends BaseCommand {
"usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
"usage: telecom set-test-call-screening-app <PACKAGE>\n" +
"usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
"usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
@@ -120,6 +122,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_REGISTER_PHONE_ACCOUNT:
runRegisterPhoneAccount();
break;
+ case COMMAND_SET_TEST_CALL_REDIRECTION_APP:
+ runSetTestCallRedirectionApp();
+ break;
case COMMAND_SET_TEST_CALL_SCREENING_APP:
runSetTestCallScreeningApp();
break;
@@ -189,6 +194,11 @@ public final class Telecom extends BaseCommand {
System.out.println("Success - " + handle + " registered.");
}
+ private void runSetTestCallRedirectionApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestDefaultCallRedirectionApp(packageName);
+ }
+
private void runSetTestCallScreeningApp() throws RemoteException {
final String packageName = nextArg();
mTelecomService.setTestDefaultCallScreeningApp(packageName);
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 01c70286cff3..bacb991012fd 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1639,10 +1639,6 @@ Landroid/widget/DigitalClock$FormatChangeObserver;-><init>(Landroid/widget/Digit
Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
Landroid/widget/ScrollBarDrawable;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
Lcom/android/ims/ImsCall;->isMultiparty()Z
Lcom/android/ims/ImsCall;->reject(I)V
@@ -1849,13 +1845,16 @@ Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lco
Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V
+Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V
Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
+Lcom/android/internal/location/ILocationProviderManager;->onReportLocation(Landroid/location/Location;)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetEnabled(Z)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetProperties(Lcom/android/internal/location/ProviderProperties;)V
Lcom/android/internal/logging/MetricsLogger;-><init>()V
Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
Lcom/android/internal/net/VpnConfig;-><init>()V
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index 65e3f25f8fb6..ac3daaf638ad 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -26,16 +26,32 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * Indicates that a class member, that is not part of the SDK, is used by apps.
- * Since the member is not part of the SDK, such use is not supported.
+ * Indicates that this non-SDK interface is used by apps. A non-SDK interface is a
+ * class member (field or method) that is not part of the public SDK. Since the
+ * member is not part of the SDK, usage by apps is not supported.
*
- * <p>This annotation acts as a heads up that changing a given method or field
+ * <h2>If you are an Android App developer</h2>
+ *
+ * This annotation indicates that you may be able to access the member, but that
+ * this access is discouraged and not supported by Android. If there is a value
+ * for {@link #maxTargetSdk()} on the annotation, access will be restricted based
+ * on the {@code targetSdkVersion} value set in your manifest.
+ *
+ * <p>Fields and methods annotated with this are likely to be restricted, changed
+ * or removed in future Android releases. If you rely on these members for
+ * functionality that is not otherwise supported by Android, consider filing a
+ * <a href="http://g.co/dev/appcompat">feature request</a>.
+ *
+ * <h2>If you are an Android OS developer</h2>
+ *
+ * This annotation acts as a heads up that changing a given method or field
* may affect apps, potentially breaking them when the next Android version is
* released. In some cases, for members that are heavily used, this annotation
* may imply restrictions on changes to the member.
*
* <p>This annotation also results in access to the member being permitted by the
- * runtime, with a warning being generated in debug builds.
+ * runtime, with a warning being generated in debug builds. Which apps can access
+ * the member is determined by the value of {@link #maxTargetSdk()}.
*
* <p>For more details, see go/UnsupportedAppUsage.
*
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 61b9d55a7fa6..5e445d14a08b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6755,8 +6755,8 @@ public class Activity extends ContextThemeWrapper
case "--autofill":
dumpAutofillManager(prefix, writer);
return;
- case "--intelligence":
- dumpIntelligenceManager(prefix, writer);
+ case "--contentcapture":
+ dumpContentCaptureManager(prefix, writer);
return;
}
}
@@ -6788,7 +6788,7 @@ public class Activity extends ContextThemeWrapper
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
dumpAutofillManager(prefix, writer);
- dumpIntelligenceManager(prefix, writer);
+ dumpContentCaptureManager(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
}
@@ -6804,12 +6804,12 @@ public class Activity extends ContextThemeWrapper
}
}
- void dumpIntelligenceManager(String prefix, PrintWriter writer) {
- final ContentCaptureManager im = getContentCaptureManager();
- if (im != null) {
- im.dump(prefix, writer);
+ void dumpContentCaptureManager(String prefix, PrintWriter writer) {
+ final ContentCaptureManager cm = getContentCaptureManager();
+ if (cm != null) {
+ cm.dump(prefix, writer);
} else {
- writer.print(prefix); writer.println("No IntelligenceManager");
+ writer.print(prefix); writer.println("No ContentCaptureManager");
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f2fb33fef466..6905cb5cea73 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2502,30 +2502,6 @@ public class AppOpsManager {
}
/**
- * Retrieve current operation state for all applications.
- *
- * @param ops The set of operations you are interested in, or null if you want all of them.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
- @SystemApi
- public List<AppOpsManager.PackageOps> getPackagesForOpStrs(String[] ops) {
- if (ops == null) {
- return getPackagesForOps(null);
- }
- final int[] opCodes = new int[ops.length];
- for (int i = 0; i < ops.length; ++i) {
- final Integer opCode = sOpStrToOp.get(ops[i]);
- if (opCode == null) {
- opCodes[i] = OP_NONE;
- } else {
- opCodes[i] = opCode;
- }
- }
- return getPackagesForOps(opCodes);
- }
-
- /**
* Retrieve current operation state for one application.
*
* @param uid The uid of the application of interest.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7312b2c8163e..67d9ad6e93c6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2974,6 +2974,15 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public String getWellbeingPackageName() {
+ try {
+ return mPM.getWellbeingPackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public boolean isPackageStateProtected(String packageName, int userId) {
try {
return mPM.isPackageStateProtected(packageName, userId);
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index dc2f9838785c..31521a369a4c 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -331,7 +331,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
writeTo.flush();
}
} catch (IOException ioe) {
- throw new RuntimeException("Error while reading/writing ", ioe);
+ Log.w(TAG, "Error while reading/writing to streams");
} finally {
IoUtils.closeQuietly(readFrom);
IoUtils.closeQuietly(writeTo);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8e54961faabc..81eac5a413a6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10239,4 +10239,73 @@ public class DevicePolicyManager {
}
return Collections.emptySet();
}
+
+ /**
+ * Returns whether the device is being used as a managed kiosk, as defined in the CDD. As of
+ * this release, these requirements are as follows:
+ * <ul>
+ * <li>The device is in Lock Task (therefore there is also a Device Owner app on the
+ * device)</li>
+ * <li>The Lock Task feature {@link DevicePolicyManager#LOCK_TASK_FEATURE_SYSTEM_INFO} is
+ * not enabled, so the system info in the status bar is not visible</li>
+ * <li>The device does not have a secure lock screen (e.g. it has no lock screen or has
+ * swipe-to-unlock)</li>
+ * <li>The device is not in the middle of an ephemeral user session</li>
+ * </ul>
+ *
+ * <p>Publicly-accessible dedicated devices don't have the same privacy model as
+ * personally-used devices. In particular, user consent popups don't make sense as a barrier to
+ * accessing persistent data on these devices since the user giving consent and the user whose
+ * data is on the device are unlikely to be the same. These consent popups prevent the true
+ * remote management of these devices.
+ *
+ * <p>This condition is not sufficient to cover APIs that would access data that only lives for
+ * the duration of the user's session, since the user has an expectation of privacy in these
+ * conditions that more closely resembles use of a personal device. In those cases, see {@link
+ * #isUnattendedManagedKiosk()}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isManagedKiosk() {
+ throwIfParentInstance("isManagedKiosk");
+ if (mService != null) {
+ try {
+ return mService.isManagedKiosk();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether the device is being used as an unattended managed kiosk, as defined in the
+ * CDD. As of this release, these requirements are as follows:
+ * <ul>
+ * <li>The device is being used as a managed kiosk, as defined in the CDD and verified at
+ * {@link #isManagedKiosk()}</li>
+ * <li>The device has not received user input for at least 30 minutes</li>
+ * </ul>
+ *
+ * <p>See {@link #isManagedKiosk()} for context. This is a stronger requirement that also
+ * ensures that the device hasn't been interacted with recently, making it an appropriate check
+ * for privacy-sensitive APIs that wouldn't be appropriate during an active user session.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isUnattendedManagedKiosk() {
+ throwIfParentInstance("isUnattendedManagedKiosk");
+ if (mService != null) {
+ try {
+ return mService.isUnattendedManagedKiosk();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 114868580ead..1ff414619be1 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -428,4 +428,7 @@ interface IDevicePolicyManager {
List<String> getCrossProfileCalendarPackages(in ComponentName admin);
boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
+
+ boolean isManagedKiosk();
+ boolean isUnattendedManagedKiosk();
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 3e2074837b8e..0afb98f1ed84 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -91,6 +91,15 @@ interface IBackupManager {
* at some point in the future.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which backup service should be enabled/disabled.
+ */
+ void setBackupEnabledForUser(int userId, boolean isEnabled);
+
+ /**
+ * {@link android.app.backup.IBackupManager.setBackupEnabledForUser} for the calling user id.
*/
void setBackupEnabled(boolean isEnabled);
@@ -120,6 +129,15 @@ interface IBackupManager {
* Report whether the backup mechanism is currently enabled.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the backup service status should be reported.
+ */
+ boolean isBackupEnabledForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.isBackupEnabledForUser} for the calling user id.
*/
boolean isBackupEnabled();
@@ -149,6 +167,15 @@ interface IBackupManager {
* method.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an immediate backup should be scheduled.
+ */
+ void backupNowForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.backupNowForUser} for the calling user id.
*/
void backupNow();
@@ -432,6 +459,12 @@ interface IBackupManager {
* <p>If this method returns zero (meaning success), the OS will attempt to backup all provided
* packages using the remote transport.
*
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an immediate backup should be requested.
+
* @param observer The {@link BackupObserver} to receive callbacks during the backup
* operation.
*
@@ -442,12 +475,29 @@ interface IBackupManager {
*
* @return Zero on success; nonzero on error.
*/
+ int requestBackupForUser(int userId, in String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags);
+
+ /**
+ * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id.
+ */
int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
int flags);
/**
* Cancel all running backups. After this call returns, no currently running backups will
* interact with the selected transport.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which backups should be cancelled.
+ */
+ void cancelBackupsForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.cancelBackups} for the calling user id.
*/
void cancelBackups();
}
diff --git a/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl b/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
new file mode 100644
index 000000000000..6cf961fad2c4
--- /dev/null
+++ b/core/java/android/app/role/IOnRoleHoldersChangedListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role;
+
+/**
+ * @hide
+ */
+oneway interface IOnRoleHoldersChangedListener {
+
+ void onRoleHoldersChanged(String roleName, int userId);
+}
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 3ca8ec04f54e..4ce0f318bad5 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -16,6 +16,7 @@
package android.app.role;
+import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManagerCallback;
/**
@@ -37,6 +38,11 @@ interface IRoleManager {
void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+ void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
+
+ void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener,
+ int userId);
+
void setRoleNamesFromController(in List<String> roleNames);
boolean addRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/OnRoleHoldersChangedListener.java b/core/java/android/app/role/OnRoleHoldersChangedListener.java
new file mode 100644
index 000000000000..5958debc86dd
--- /dev/null
+++ b/core/java/android/app/role/OnRoleHoldersChangedListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
+
+/**
+ * Listener for role holder changes.
+ *
+ * @hide
+ */
+@SystemApi
+public interface OnRoleHoldersChangedListener {
+
+ /**
+ * Called when the holders of roles are changed.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param user the user for this role holder change
+ */
+ void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user);
+}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 7cb245adb609..5d101ab479ac 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -22,6 +22,8 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -29,8 +31,12 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.List;
import java.util.concurrent.Executor;
@@ -124,6 +130,13 @@ public final class RoleManager {
@NonNull
private final IRoleManager mService;
+ @GuardedBy("mListenersLock")
+ @NonNull
+ private final SparseArray<ArrayMap<OnRoleHoldersChangedListener,
+ OnRoleHoldersChangedListenerDelegate>> mListeners = new SparseArray<>();
+ @NonNull
+ private final Object mListenersLock = new Object();
+
/**
* @hide
*/
@@ -145,8 +158,6 @@ public final class RoleManager {
* @param roleName the name of requested role
*
* @return the {@code Intent} to prompt user to grant the role
- *
- * @throws IllegalArgumentException if {@code role} is {@code null} or empty
*/
@NonNull
public Intent createRequestRoleIntent(@NonNull String roleName) {
@@ -163,8 +174,6 @@ public final class RoleManager {
* @param roleName the name of role to checking for
*
* @return whether the role is available in the system
- *
- * @throws IllegalArgumentException if the role name is {@code null} or empty
*/
public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -181,8 +190,6 @@ public final class RoleManager {
* @param roleName the name of the role to check for
*
* @return whether the calling application is holding the role
- *
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
*/
public boolean isRoleHeld(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -203,8 +210,6 @@ public final class RoleManager {
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
*
* @hide
@@ -212,6 +217,7 @@ public final class RoleManager {
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public List<String> getRoleHolders(@NonNull String roleName) {
return getRoleHoldersAsUser(roleName, Process.myUserHandle());
}
@@ -228,8 +234,6 @@ public final class RoleManager {
*
* @return a list of package names of the role holders, or an empty list if none.
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -239,6 +243,7 @@ public final class RoleManager {
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkNotNull(user, "user cannot be null");
@@ -263,8 +268,6 @@ public final class RoleManager {
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -273,6 +276,7 @@ public final class RoleManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
@NonNull RoleManagerCallback callback) {
@@ -302,8 +306,6 @@ public final class RoleManager {
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -312,6 +314,7 @@ public final class RoleManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
@NonNull RoleManagerCallback callback) {
@@ -340,8 +343,6 @@ public final class RoleManager {
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
- * @throws IllegalArgumentException if the role name is {@code null} or empty.
- *
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
@@ -350,6 +351,7 @@ public final class RoleManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @TestApi
public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
@CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -365,6 +367,96 @@ public final class RoleManager {
}
/**
+ * Add a listener to observe role holder changes
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @param executor the {@code Executor} to call the listener on.
+ * @param listener the listener to be added
+ * @param user the user to add the listener for
+ *
+ * @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
+ @SystemApi
+ public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(user, "user cannot be null");
+ int userId = user.getIdentifier();
+ synchronized (mListenersLock) {
+ ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
+ mListeners.get(userId);
+ if (listeners == null) {
+ listeners = new ArrayMap<>();
+ mListeners.put(userId, listeners);
+ } else {
+ if (listeners.containsKey(listener)) {
+ return;
+ }
+ }
+ OnRoleHoldersChangedListenerDelegate listenerDelegate =
+ new OnRoleHoldersChangedListenerDelegate(executor, listener);
+ try {
+ mService.addOnRoleHoldersChangedListenerAsUser(listenerDelegate, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ listeners.put(listener, listenerDelegate);
+ }
+ }
+
+ /**
+ * Remove a listener observing role holder changes
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
+ * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @param listener the listener to be removed
+ * @param user the user to remove the listener for
+ *
+ * @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener,
+ * UserHandle)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
+ @SystemApi
+ public void removeOnRoleHoldersChangedListenerAsUser(
+ @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(user, "user cannot be null");
+ int userId = user.getIdentifier();
+ synchronized (mListenersLock) {
+ ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
+ mListeners.get(userId);
+ if (listeners == null) {
+ return;
+ }
+ OnRoleHoldersChangedListenerDelegate listenerDelegate = listeners.get(listener);
+ if (listenerDelegate == null) {
+ return;
+ }
+ try {
+ mService.removeOnRoleHoldersChangedListenerAsUser(listenerDelegate,
+ user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ listeners.remove(listener);
+ if (listeners.isEmpty()) {
+ mListeners.remove(userId);
+ }
+ }
+ }
+
+ /**
* Set the names of all the available roles. Should only be called from
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
@@ -373,8 +465,6 @@ public final class RoleManager {
*
* @param roleNames the names of all the available roles
*
- * @throws IllegalArgumentException if the list of role names is {@code null}.
- *
* @hide
*/
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@@ -402,8 +492,6 @@ public final class RoleManager {
* @return whether the operation was successful, and will also be {@code true} if a matching
* role holder is already found.
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHolders(String)
* @see #removeRoleHolderFromController(String, String)
*
@@ -436,8 +524,6 @@ public final class RoleManager {
* @return whether the operation was successful, and will also be {@code true} if no matching
* role holder was found to remove.
*
- * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
- *
* @see #getRoleHolders(String)
* @see #addRoleHolderFromController(String, String)
*
@@ -489,4 +575,31 @@ public final class RoleManager {
}
}
}
+
+ private static class OnRoleHoldersChangedListenerDelegate
+ extends IOnRoleHoldersChangedListener.Stub {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final OnRoleHoldersChangedListener mListener;
+
+ OnRoleHoldersChangedListenerDelegate(@NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(PooledLambda.obtainRunnable(
+ OnRoleHoldersChangedListener::onRoleHoldersChanged, mListener, roleName,
+ UserHandle.of(userId)));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/role/RoleManagerCallback.java b/core/java/android/app/role/RoleManagerCallback.java
index ca68ebc98303..d9f0a6c97f4d 100644
--- a/core/java/android/app/role/RoleManagerCallback.java
+++ b/core/java/android/app/role/RoleManagerCallback.java
@@ -17,6 +17,7 @@
package android.app.role;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
/**
* Callback for a {@link RoleManager} request.
@@ -24,6 +25,7 @@ import android.annotation.SystemApi;
* @hide
*/
@SystemApi
+@TestApi
public interface RoleManagerCallback {
/**
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index da659415aa12..6704a45fb07a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
@@ -2978,7 +2979,12 @@ public abstract class ContentResolver implements ContentInterface {
}
}
- /** {@hide} */
+ /**
+ * Put the cache with the key.
+ *
+ * @param key the key to add
+ * @param value the value to add
+ */
public void putCache(Uri key, Bundle value) {
try {
getContentService().putCache(mContext.getPackageName(), key, value,
@@ -2988,7 +2994,13 @@ public abstract class ContentResolver implements ContentInterface {
}
}
- /** {@hide} */
+ /**
+ * Get the cache with the key.
+ *
+ * @param key the key to get the value
+ * @return the matched value. If the key doesn't exist, will return null.
+ * @see #putCache(Uri, Bundle)
+ */
public Bundle getCache(Uri key) {
try {
final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
@@ -3176,7 +3188,14 @@ public abstract class ContentResolver implements ContentInterface {
return mContext.getUserId();
}
- /** @hide */
+ /**
+ * Get the system drawable of the mime type.
+ *
+ * @param mimeType the requested mime type
+ * @return the matched drawable
+ * @hide
+ */
+ @SystemApi
public Drawable getTypeDrawable(String mimeType) {
return MimeIconUtils.loadMimeIcon(mContext, mimeType);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b6f9d15cc20b..2f0618cf64b3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5267,8 +5267,6 @@ public class Intent implements Parcelable, Cloneable {
* Used as a boolean extra field in {@link #ACTION_CHOOSER} intents to specify
* whether to show the chooser or not when there is only one application available
* to choose from.
- *
- * @hide
*/
public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE =
"android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 1c564f3708df..740fd7f963f9 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -16,6 +16,8 @@
package android.content.pm;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -66,7 +68,30 @@ public class CrossProfileApps {
mContext.getIApplicationThread(),
mContext.getPackageName(),
component,
- targetUser);
+ targetUser.getIdentifier(),
+ true);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts the specified activity of the caller package in the specified profile if the caller
+ * has {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} permission and
+ * both the caller and target user profiles are in the same user group.
+ *
+ * @param component The ComponentName of the activity to launch. It must be exported.
+ * @param targetUser The UserHandle of the profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
+ public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+ try {
+ mService.startActivityAsUser(mContext.getIApplicationThread(),
+ mContext.getPackageName(), component, targetUser.getIdentifier(), false);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index bc2f92a9bf51..d2d66cbda610 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -28,6 +28,6 @@ import android.os.UserHandle;
*/
interface ICrossProfileApps {
void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
- in ComponentName component, in UserHandle user);
+ in ComponentName component, int userId, boolean launchMainActivity);
List<UserHandle> getTargetUserProfiles(in String callingPackage);
} \ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dbea821fab2b..eea2b8873fe7 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -675,6 +675,8 @@ interface IPackageManager {
String getSystemTextClassifierPackageName();
+ String getWellbeingPackageName();
+
boolean isPackageStateProtected(String packageName, int userId);
void sendDeviceCustomizationReadyBroadcast();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f40be84b180b..da39b6394f8f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1399,6 +1399,16 @@ public abstract class PackageManager {
public static final int DELETE_DONT_KILL_APP = 0x00000008;
/**
+ * Flag parameter for {@link #deletePackage} to indicate that any
+ * contributed media should also be deleted during this uninstall. The
+ * meaning of "contributed" means it won't automatically be deleted when the
+ * app is uninstalled.
+ *
+ * @hide
+ */
+ public static final int DELETE_CONTRIBUTED_MEDIA = 0x00000010;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that package deletion
* should be chatty.
*
@@ -6434,6 +6444,17 @@ public abstract class PackageManager {
}
/**
+ * @return the wellbeing app package name, or null if it's not defined by the OEM.
+ *
+ * @hide
+ */
+ @SystemApi
+ public String getWellbeingPackageName() {
+ throw new UnsupportedOperationException(
+ "getWellbeingPackageName not implemented in subclass");
+ }
+
+ /**
* @return whether a given package's state is protected, e.g. package cannot be disabled,
* suspended, hidden or force stopped.
*
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index b49c4476e82d..5db9f506e6e4 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -53,6 +53,8 @@ public abstract class PackageManagerInternal {
public static final int PACKAGE_BROWSER = 4;
public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
+ public static final int PACKAGE_WELLBEING = 7;
+ public static final int PACKAGE_DOCUMENTER = 8;
@IntDef(value = {
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
@@ -61,6 +63,8 @@ public abstract class PackageManagerInternal {
PACKAGE_BROWSER,
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
PACKAGE_PERMISSION_CONTROLLER,
+ PACKAGE_WELLBEING,
+ PACKAGE_DOCUMENTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index d9d6b5f87eac..bb8c92dba71a 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -181,6 +181,28 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
@TestApi
public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+ /**
+ * Additional flag for {${link #protectionLevel}, corresponding
+ * to the <code>wellbeing</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
+
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding to the
+ * {@code documenter} value of {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000;
+
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -197,6 +219,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_OEM,
PROTECTION_FLAG_VENDOR_PRIVILEGED,
PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+ PROTECTION_FLAG_WELLBEING,
+ PROTECTION_FLAG_DOCUMENTER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -386,6 +410,12 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
protLevel += "|textClassifier";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
+ protLevel += "|wellbeing";
+ }
+ if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
+ protLevel += "|documenter";
+ }
return protLevel;
}
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 5afc8a9721b4..a607a9ff682b 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,15 +22,15 @@ package android.content.pm;
*/
public class SharedLibraryNames {
- public static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+ static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
- public static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+ static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
- public static final String ANDROID_TEST_BASE = "android.test.base";
+ static final String ANDROID_TEST_BASE = "android.test.base";
- public static final String ANDROID_TEST_MOCK = "android.test.mock";
+ static final String ANDROID_TEST_MOCK = "android.test.mock";
- public static final String ANDROID_TEST_RUNNER = "android.test.runner";
+ static final String ANDROID_TEST_RUNNER = "android.test.runner";
public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
}
diff --git a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
index 9490e276f228..2faf3adb34b6 100644
--- a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
+++ b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
@@ -22,6 +22,8 @@ import android.os.RemoteCallback;
* Interface for communication with the permission presenter service.
*
* @hide
+ * @deprecated Only available to keep
+ * android.permissionpresenterservice.RuntimePermissionPresenterService functional
*/
oneway interface IRuntimePermissionPresenter {
void getAppPermissions(String packageName, in RemoteCallback callback);
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
index 352e8ad106b8..9fa863c75fdf 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
@@ -29,7 +29,11 @@ import android.os.Parcelable;
* coarse and fine platform permissions.
*
* @hide
+ *
+ * @deprecated Not used anymore. Use {@link android.permission.RuntimePermissionPresentationInfo}
+ * instead
*/
+@Deprecated
@SystemApi
public final class RuntimePermissionPresentationInfo implements Parcelable {
private static final int FLAG_GRANTED = 1 << 0;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0c44a566b48a..8a5f43de6883 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -931,7 +931,7 @@ public final class NetworkCapabilities implements Parcelable {
* Returns a transport-specific information container. The application may cast this
* container to a concrete sub-class based on its knowledge of the network request. The
* application should be able to deal with a {@code null} return value or an invalid case,
- * e.g. use {@code instanceof} operation to verify expected type.
+ * e.g. use {@code instanceof} operator to verify expected type.
*
* @return A concrete implementation of the {@link TransportInfo} class or null if not
* available for the network.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 2ae796c1ec56..d45fa11de639 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -916,23 +916,49 @@ public class Binder implements IBinder {
private static native long getNativeBBinderHolder();
private static native long getFinalizer();
+ /**
+ * By default, we use the calling uid since we can always trust it.
+ */
+ private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider =
+ Binder::getCallingUid;
+
+ /**
+ * Sets the work source provider.
+ *
+ * <li>The callback is global. Only fast operations should be done to avoid thread
+ * contentions.
+ * <li>The callback implementation needs to handle synchronization if needed. The methods on the
+ * callback can be called concurrently.
+ * <li>The callback is called on the critical path of the binder transaction so be careful about
+ * performance.
+ * <li>Never execute another binder transaction inside the callback.
+ * @hide
+ */
+ public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) {
+ if (workSourceProvider == null) {
+ throw new IllegalArgumentException("workSourceProvider cannot be null");
+ }
+ sWorkSourceProvider = workSourceProvider;
+ }
+
// Entry point from android_util_Binder.cpp's onTransact
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
- final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
+ final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid();
+ final long origWorkSource = ThreadLocalWorkSource.setUid(workSourceUid);
try {
- return execTransactInternal(code, dataObj, replyObj, flags);
+ return execTransactInternal(code, dataObj, replyObj, flags, workSourceUid);
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
}
}
private boolean execTransactInternal(int code, long dataObj, long replyObj,
- int flags) {
+ int flags, int workSourceUid) {
// Make sure the observer won't change while processing a transaction.
final BinderInternal.Observer observer = sObserver;
final CallSession callSession =
- observer != null ? observer.callStarted(this, code) : null;
+ observer != null ? observer.callStarted(this, code, workSourceUid) : null;
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
// theoretically, we should call transact, which will call onTransact,
@@ -971,10 +997,11 @@ public class Binder implements IBinder {
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
+ if (observer != null) {
+ observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
+ }
}
checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
- int replySizeBytes = reply.dataSize();
- int requestSizeBytes = data.dataSize();
reply.recycle();
data.recycle();
@@ -984,9 +1011,6 @@ public class Binder implements IBinder {
// to the main transaction loop to wait for another incoming transaction. Either
// way, strict mode begone!
StrictMode.clearGatheredViolations();
- if (observer != null) {
- observer.callEnded(callSession, requestSizeBytes, replySizeBytes);
- }
return res;
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 292543c4a19a..9fea873d34a8 100644..100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1226,7 +1226,8 @@ public class Build {
* null (if, for instance, the radio is not currently on).
*/
public static String getRadioVersion() {
- return SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION, null);
+ String propVal = SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION);
+ return TextUtils.isEmpty(propVal) ? null : propVal;
}
private static String getString(String property) {
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 28ea553c8800..567efa7b7afb 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -66,6 +66,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
@@ -717,6 +720,57 @@ public class FileUtils {
}
/**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(@NonNull File file, @NonNull String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ try (FileInputStream in = new FileInputStream(file)) {
+ return digest(in, algorithm);
+ }
+ }
+
+ /**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ // TODO: implement kernel optimizations
+ return digestInternalUserspace(in, algorithm);
+ }
+
+ /**
+ * Compute the digest of the given file using the requested algorithm.
+ *
+ * @param algorithm Any valid algorithm accepted by
+ * {@link MessageDigest#getInstance(String)}.
+ * @hide
+ */
+ public static byte[] digest(FileDescriptor fd, String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ // TODO: implement kernel optimizations
+ return digestInternalUserspace(new FileInputStream(fd), algorithm);
+ }
+
+ private static byte[] digestInternalUserspace(InputStream in, String algorithm)
+ throws IOException, NoSuchAlgorithmException {
+ final MessageDigest digest = MessageDigest.getInstance(algorithm);
+ try (DigestInputStream digestStream = new DigestInputStream(in, digest)) {
+ final byte[] buffer = new byte[8192];
+ while (digestStream.read(buffer) != -1) {
+ }
+ }
+ return digest.digest();
+ }
+
+ /**
* Delete older files in a directory until only those matching the given
* constraints remain.
*
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c9c42058bad0..be8cf0e9137a 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -18,7 +18,6 @@
package android.os;
import android.net.InterfaceConfiguration;
-import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.ITetheringStatsProvider;
import android.net.Network;
@@ -47,11 +46,6 @@ interface INetworkManagementService
void unregisterObserver(INetworkManagementEventObserver obs);
/**
- * Retrieve an INetd to talk to netd.
- */
- INetd getNetdService();
-
- /**
* Returns a list of currently known network interfaces
*/
String[] listInterfaces();
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 2cb5aeeeb2e6..d55489a08136 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -200,4 +200,7 @@ public abstract class PowerManagerInternal {
* PowerHint defined in android/hardware/power/<version 1.0 & up>/IPower.h
*/
public abstract void powerHint(int hintId, int data);
+
+ /** Returns whether there hasn't been a user activity event for the given number of ms. */
+ public abstract boolean wasDeviceIdleFor(long ms);
}
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 5499181c3cdb..eee2b520afa8 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -70,6 +70,7 @@ public final class Temperature implements Parcelable {
TYPE_BCL_VOLTAGE,
TYPE_BCL_CURRENT,
TYPE_BCL_PERCENTAGE,
+ TYPE_NPU,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -85,6 +86,7 @@ public final class Temperature implements Parcelable {
public static final int TYPE_BCL_VOLTAGE = TemperatureType.BCL_VOLTAGE;
public static final int TYPE_BCL_CURRENT = TemperatureType.BCL_CURRENT;
public static final int TYPE_BCL_PERCENTAGE = TemperatureType.BCL_PERCENTAGE;
+ public static final int TYPE_NPU = TemperatureType.NPU;
/**
* Verify a valid temperature type.
@@ -92,7 +94,7 @@ public final class Temperature implements Parcelable {
* @return true if a temperature type is valid otherwise false.
*/
public static boolean isValidType(@Type int type) {
- return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
+ return type >= TYPE_UNKNOWN && type <= TYPE_NPU;
}
/**
diff --git a/core/java/android/permission/IRuntimePermissionPresenter.aidl b/core/java/android/permission/IRuntimePermissionPresenter.aidl
new file mode 100644
index 000000000000..e95428ab2b02
--- /dev/null
+++ b/core/java/android/permission/IRuntimePermissionPresenter.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.os.RemoteCallback;
+
+/**
+ * Interface for communication with the permission presenter service.
+ *
+ * @hide
+ */
+oneway interface IRuntimePermissionPresenter {
+ void getAppPermissions(String packageName, in RemoteCallback callback);
+ void revokeRuntimePermission(String packageName, String permissionName);
+ void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, in RemoteCallback callback);
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl b/core/java/android/permission/RuntimePermissionPresentationInfo.aidl
index f96a32f68bd8..533c1ffc3bb8 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.content.pm.permission;
+package android.permission;
parcelable RuntimePermissionPresentationInfo;
diff --git a/core/java/android/permission/RuntimePermissionPresentationInfo.java b/core/java/android/permission/RuntimePermissionPresentationInfo.java
new file mode 100644
index 000000000000..ed7b05c83046
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is to be presented in the UI. A single runtime permission
+ * presented to the user may correspond to multiple platform defined
+ * permissions, e.g. the location permission may control both the
+ * coarse and fine platform permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionPresentationInfo implements Parcelable {
+ private static final int FLAG_GRANTED = 1 << 0;
+ private static final int FLAG_STANDARD = 1 << 1;
+
+ private final CharSequence mLabel;
+ private final int mFlags;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param label The permission label.
+ * @param granted Whether the permission is granted.
+ * @param standard Whether this is a platform-defined permission.
+ */
+ public RuntimePermissionPresentationInfo(CharSequence label,
+ boolean granted, boolean standard) {
+ mLabel = label;
+ int flags = 0;
+ if (granted) {
+ flags |= FLAG_GRANTED;
+ }
+ if (standard) {
+ flags |= FLAG_STANDARD;
+ }
+ mFlags = flags;
+ }
+
+ private RuntimePermissionPresentationInfo(Parcel parcel) {
+ mLabel = parcel.readCharSequence();
+ mFlags = parcel.readInt();
+ }
+
+ /**
+ * @return Whether the permission is granted.
+ */
+ public boolean isGranted() {
+ return (mFlags & FLAG_GRANTED) != 0;
+ }
+
+ /**
+ * @return Whether the permission is platform-defined.
+ */
+ public boolean isStandard() {
+ return (mFlags & FLAG_STANDARD) != 0;
+ }
+
+ /**
+ * Gets the permission label.
+ *
+ * @return The label.
+ */
+ public @NonNull CharSequence getLabel() {
+ return mLabel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeCharSequence(mLabel);
+ parcel.writeInt(mFlags);
+ }
+
+ public static final Creator<RuntimePermissionPresentationInfo> CREATOR =
+ new Creator<RuntimePermissionPresentationInfo>() {
+ public RuntimePermissionPresentationInfo createFromParcel(Parcel source) {
+ return new RuntimePermissionPresentationInfo(source);
+ }
+
+ public RuntimePermissionPresentationInfo[] newArray(int size) {
+ return new RuntimePermissionPresentationInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
index 73addb70ea05..3ce3c9db2623 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/permission/RuntimePermissionPresenter.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package android.content.pm.permission;
+package android.permission;
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -30,7 +31,6 @@ import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
-import android.permissionpresenterservice.RuntimePermissionPresenterService;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -60,20 +60,32 @@ public final class RuntimePermissionPresenter {
* @hide
*/
public static final String KEY_RESULT =
- "android.content.pm.permission.RuntimePermissionPresenter.key.result";
+ "android.permission.RuntimePermissionPresenter.key.result";
/**
- * Listener for delivering a result.
+ * Listener for delivering the result of {@link #getAppPermissions}.
*/
- public static abstract class OnResultCallback {
+ public interface OnGetAppPermissionResultCallback {
/**
- * The result for {@link #getAppPermissions(String, OnResultCallback, Handler)}.
+ * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
+ * Handler)}.
+ *
* @param permissions The permissions list.
*/
- public void onGetAppPermissions(@NonNull
- List<RuntimePermissionPresentationInfo> permissions) {
- /* do nothing - stub */
- }
+ void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
+ }
+
+ /**
+ * Listener for delivering the result of {@link #countPermissionApps}.
+ */
+ public interface OnCountPermissionAppsResultCallback {
+ /**
+ * The result for {@link #countPermissionApps(List, boolean,
+ * OnCountPermissionAppsResultCallback, Handler)}.
+ *
+ * @param numApps The number of apps that have one of the permissions
+ */
+ void onCountPermissionApps(int numApps);
}
private static final Object sLock = new Object();
@@ -110,7 +122,7 @@ public final class RuntimePermissionPresenter {
* @param handler Handler on which to invoke the callback.
*/
public void getAppPermissions(@NonNull String packageName,
- @NonNull OnResultCallback callback, @Nullable Handler handler) {
+ @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
checkNotNull(packageName);
checkNotNull(callback);
@@ -133,6 +145,25 @@ public final class RuntimePermissionPresenter {
mRemoteService, packageName, permissionName));
}
+ /**
+ * Count how many apps have one of a set of permissions.
+ *
+ * @param permissionNames The permissions the app might have
+ * @param countOnlyGranted Count an app only if the permission is granted to the app
+ * @param countSystem Also count system apps
+ * @param callback Callback to receive the result
+ * @param handler Handler on which to invoke the callback
+ */
+ public void countPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem,
+ @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+ checkCollectionElementsNotNull(permissionNames, "permissionNames");
+ checkNotNull(callback);
+
+ mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
+ mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
+ }
+
private static final class RemoteService
extends Handler implements ServiceConnection {
private static final long UNBIND_TIMEOUT_MILLIS = 10000;
@@ -152,7 +183,7 @@ public final class RuntimePermissionPresenter {
@GuardedBy("mLock")
private boolean mBound;
- public RemoteService(Context context) {
+ RemoteService(Context context) {
super(context.getMainLooper(), null, false);
mContext = context;
}
@@ -188,7 +219,7 @@ public final class RuntimePermissionPresenter {
}
private void getAppPermissions(@NonNull String packageName,
- @NonNull OnResultCallback callback, @Nullable Handler handler) {
+ @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
final IRuntimePermissionPresenter remoteInstance;
synchronized (mLock) {
remoteInstance = mRemoteInstance;
@@ -245,6 +276,45 @@ public final class RuntimePermissionPresenter {
}
}
+ private void countPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem,
+ @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+ final IRuntimePermissionPresenter remoteInstance;
+
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+
+ try {
+ remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
+ new RemoteCallback(result -> {
+ final int numApps;
+ if (result != null) {
+ numApps = result.getInt(KEY_RESULT);
+ } else {
+ numApps = 0;
+ }
+
+ if (handler != null) {
+ handler.post(() -> callback.onCountPermissionApps(numApps));
+ } else {
+ callback.onCountPermissionApps(numApps);
+ }
+ }, this));
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error counting permission apps", re);
+ }
+
+ scheduleUnbind();
+
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
private void unbind() {
synchronized (mLock) {
if (mBound) {
diff --git a/core/java/android/permission/RuntimePermissionPresenterService.java b/core/java/android/permission/RuntimePermissionPresenterService.java
new file mode 100644
index 000000000000..81ec7bed19af
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresenterService.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+
+import java.util.List;
+
+/**
+ * This service presents information regarding runtime permissions that is
+ * used for presenting them in the UI. Runtime permissions are presented as
+ * a single permission in the UI but may be composed of several individual
+ * permissions.
+ *
+ * @see RuntimePermissionPresenter
+ * @see RuntimePermissionPresentationInfo
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class RuntimePermissionPresenterService extends Service {
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest for the system to recognize it as a runtime permission
+ * presenter service.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.permission.RuntimePermissionPresenterService";
+
+ // No need for locking - always set first and never modified
+ private Handler mHandler;
+
+ @Override
+ public final void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ mHandler = new Handler(base.getMainLooper());
+ }
+
+ /**
+ * Gets the runtime permissions for an app.
+ *
+ * @param packageName The package for which to query.
+ *
+ * @return descriptions of the runtime permissions of the app
+ */
+ public abstract @NonNull List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+ @NonNull String packageName);
+
+ /**
+ * Revokes the permission {@code permissionName} for app {@code packageName}
+ *
+ * @param packageName The package for which to revoke
+ * @param permissionName The permission to revoke
+ */
+ public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName);
+
+ /**
+ * Count how many apps have one of a set of permissions.
+ *
+ * @param permissionNames The permissions the app might have
+ * @param countOnlyGranted Count an app only if the permission is granted to the app
+ * @param countSystem Also count system apps
+ *
+ * @return the number of apps that have one of the permissions
+ */
+ public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem);
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IRuntimePermissionPresenter.Stub() {
+ @Override
+ public void getAppPermissions(String packageName, RemoteCallback callback) {
+ checkNotNull(packageName, "packageName");
+ checkNotNull(callback, "callback");
+
+ mHandler.sendMessage(
+ obtainMessage(
+ RuntimePermissionPresenterService::getAppPermissions,
+ RuntimePermissionPresenterService.this, packageName, callback));
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permissionName) {
+ checkNotNull(packageName, "packageName");
+ checkNotNull(permissionName, "permissionName");
+
+ mHandler.sendMessage(
+ obtainMessage(
+ RuntimePermissionPresenterService::onRevokeRuntimePermission,
+ RuntimePermissionPresenterService.this, packageName,
+ permissionName));
+ }
+
+ @Override
+ public void countPermissionApps(List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, RemoteCallback callback) {
+ checkCollectionElementsNotNull(permissionNames, "permissionNames");
+ checkNotNull(callback, "callback");
+
+ mHandler.sendMessage(
+ obtainMessage(
+ RuntimePermissionPresenterService::countPermissionApps,
+ RuntimePermissionPresenterService.this, permissionNames,
+ countOnlyGranted, countSystem, callback));
+ }
+ };
+ }
+
+ private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+ List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+ if (permissions != null && !permissions.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
+ }
+ }
+
+ private void countPermissionApps(@NonNull List<String> permissionNames,
+ boolean countOnlyGranted, boolean countSystem, @NonNull RemoteCallback callback) {
+ int numApps = onCountPermissionApps(permissionNames, countOnlyGranted, countSystem);
+
+ Bundle result = new Bundle();
+ result.putInt(RuntimePermissionPresenter.KEY_RESULT, numApps);
+ callback.sendResult(result);
+ }
+}
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index a41a64422038..2b3f0f525904 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.permission.IRuntimePermissionPresenter;
import android.content.pm.permission.RuntimePermissionPresentationInfo;
-import android.content.pm.permission.RuntimePermissionPresenter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -40,11 +39,13 @@ import java.util.List;
* a single permission in the UI but may be composed of several individual
* permissions.
*
- * @see RuntimePermissionPresenter
* @see RuntimePermissionPresentationInfo
*
* @hide
+ *
+ * @deprecated use {@link android.permission.RuntimePermissionPresenterService} instead
*/
+@Deprecated
@SystemApi
public abstract class RuntimePermissionPresenterService extends Service {
@@ -56,6 +57,9 @@ public abstract class RuntimePermissionPresenterService extends Service {
public static final String SERVICE_INTERFACE =
"android.permissionpresenterservice.RuntimePermissionPresenterService";
+ private static final String KEY_RESULT =
+ "android.content.pm.permission.RuntimePermissionPresenter.key.result";
+
// No need for locking - always set first and never modified
private Handler mHandler;
@@ -112,7 +116,7 @@ public abstract class RuntimePermissionPresenterService extends Service {
List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
if (permissions != null && !permissions.isEmpty()) {
Bundle result = new Bundle();
- result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+ result.putParcelableList(KEY_RESULT, permissions);
callback.sendResult(result);
} else {
callback.sendResult(null);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ff772287de25..a8726e9de3d0 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -244,18 +244,34 @@ public final class DocumentsContract {
* Get string array identifies the type or types of metadata returned
* using DocumentsContract#getDocumentMetadata.
*
- * @see #getDocumentMetadata(ContentResolver, Uri)
+ * @see #getDocumentMetadata(ContentInterface, Uri)
*/
public static final String METADATA_TYPES = "android:documentMetadataTypes";
/**
* Get Exif information using DocumentsContract#getDocumentMetadata.
*
- * @see #getDocumentMetadata(ContentResolver, Uri)
+ * @see #getDocumentMetadata(ContentInterface, Uri)
*/
public static final String METADATA_EXIF = "android:documentExif";
/**
+ * Get total count of all documents currently stored under the given
+ * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TREE_COUNT = "android:metadataTreeCount";
+
+ /**
+ * Get total size of all documents currently stored under the given
+ * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
+ *
+ * @see #getDocumentMetadata(ContentInterface, Uri)
+ */
+ public static final String METADATA_TREE_SIZE = "android:metadataTreeSize";
+
+ /**
* Constants related to a document, including {@link Cursor} column names
* and flags.
* <p>
@@ -519,7 +535,7 @@ public final class DocumentsContract {
* using DocumentsContract#getDocumentMetadata
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#getDocumentMetadata(ContentResolver, Uri)
+ * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
*/
public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
}
@@ -1259,6 +1275,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size,
+ CancellationSignal signal) throws FileNotFoundException {
+ return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal);
+ }
+
/**
* Create a new document with given MIME type and display name.
*
@@ -1285,6 +1307,11 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
+ String mimeType, String displayName) throws FileNotFoundException {
+ return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName);
+ }
/**
* Test if a document is descendant (child, grandchild, etc) from the given
@@ -1318,6 +1345,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri,
+ Uri childDocumentUri) throws FileNotFoundException {
+ return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri);
+ }
+
/**
* Change the display name of an existing document.
* <p>
@@ -1349,6 +1382,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Uri renameDocument(ContentResolver content, Uri documentUri,
+ String displayName) throws FileNotFoundException {
+ return renameDocument((ContentInterface) content, documentUri, displayName);
+ }
+
/**
* Delete the given document.
*
@@ -1371,6 +1410,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static boolean deleteDocument(ContentResolver content, Uri documentUri)
+ throws FileNotFoundException {
+ return deleteDocument((ContentInterface) content, documentUri);
+ }
+
/**
* Copies the given document.
*
@@ -1396,6 +1441,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri,
+ Uri targetParentDocumentUri) throws FileNotFoundException {
+ return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri);
+ }
+
/**
* Moves the given document under a new parent.
*
@@ -1423,6 +1474,13 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri,
+ Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
+ return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri,
+ targetParentDocumentUri);
+ }
+
/**
* Removes the given document from a parent directory.
*
@@ -1450,6 +1508,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static boolean removeDocument(ContentResolver content, Uri documentUri,
+ Uri parentDocumentUri) throws FileNotFoundException {
+ return removeDocument((ContentInterface) content, documentUri, parentDocumentUri);
+ }
+
/**
* Ejects the given root. It throws {@link IllegalStateException} when ejection failed.
*
@@ -1467,6 +1531,11 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static void ejectRoot(ContentResolver content, Uri rootUri) {
+ ejectRoot((ContentInterface) content, rootUri);
+ }
+
/**
* Returns metadata associated with the document. The type of metadata returned
* is specific to the document type. For example the data returned for an image
@@ -1512,6 +1581,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri)
+ throws FileNotFoundException {
+ return getDocumentMetadata((ContentInterface) content, documentUri);
+ }
+
/**
* Finds the canonical path from the top of the document tree.
*
@@ -1543,6 +1618,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static Path findDocumentPath(ContentResolver content, Uri treeUri)
+ throws FileNotFoundException {
+ return findDocumentPath((ContentInterface) content, treeUri);
+ }
+
/**
* Creates an intent for obtaining a web link for the specified document.
*
@@ -1616,6 +1697,12 @@ public final class DocumentsContract {
}
}
+ /** @removed */
+ public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri,
+ Bundle options) throws FileNotFoundException {
+ return createWebLinkIntent((ContentInterface) content, uri, options);
+ }
+
/**
* Open the given image for thumbnail purposes, using any embedded EXIF
* thumbnail if available, and providing orientation hints from the parent
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 14511657f4d0..f38f74046934 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -23,7 +23,6 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
@@ -50,6 +49,7 @@ import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
@@ -70,6 +70,7 @@ import java.io.OutputStream;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* The Media provider contains meta data for all available media on both internal
@@ -1046,6 +1047,20 @@ public final class MediaStore {
getContentUri("external");
/**
+ * The MIME type for this table.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/download";
+
+ /**
+ * Regex that matches paths that needs to be considered part of downloads collection.
+ * @hide
+ */
+ public static final Pattern PATTERN_DOWNLOADS_FILE = Pattern.compile(
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/.+");
+ private static final Pattern PATTERN_DOWNLOADS_DIRECTORY = Pattern.compile(
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/?");
+
+ /**
* Get the content:// style URI for the downloads table on the
* given volume.
*
@@ -1061,6 +1076,16 @@ public final class MediaStore {
public static Uri getContentUriForPath(@NonNull String path) {
return getContentUri(getVolumeNameForPath(path));
}
+
+ /** @hide */
+ public static boolean isDownload(@NonNull String path) {
+ return PATTERN_DOWNLOADS_FILE.matcher(path).matches();
+ }
+
+ /** @hide */
+ public static boolean isDownloadDir(@NonNull String path) {
+ return PATTERN_DOWNLOADS_DIRECTORY.matcher(path).matches();
+ }
}
private static String getVolumeNameForPath(@NonNull String path) {
@@ -2882,18 +2907,24 @@ public final class MediaStore {
*
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
- public static @BytesLong long getContributedMediaSize(Context context, String packageName) {
- try (ContentProviderClient client = context.getContentResolver()
- .acquireContentProviderClient(AUTHORITY)) {
- final Bundle in = new Bundle();
- in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
- final Bundle out = client.call(GET_CONTRIBUTED_MEDIA_CALL, null, in);
- return out.getLong(Intent.EXTRA_INDEX);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ public static @BytesLong long getContributedMediaSize(Context context, String packageName,
+ UserHandle user) throws IOException {
+ final UserManager um = context.getSystemService(UserManager.class);
+ if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
+ try {
+ final ContentResolver resolver = context
+ .createPackageContextAsUser(packageName, 0, user).getContentResolver();
+ final Bundle in = new Bundle();
+ in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ final Bundle out = resolver.call(AUTHORITY, GET_CONTRIBUTED_MEDIA_CALL, null, in);
+ return out.getLong(Intent.EXTRA_INDEX);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ } else {
+ throw new IOException("User " + user + " must be unlocked and running");
}
}
@@ -2904,17 +2935,23 @@ public final class MediaStore {
*
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
- public static void deleteContributedMedia(Context context, String packageName) {
- try (ContentProviderClient client = context.getContentResolver()
- .acquireContentProviderClient(AUTHORITY)) {
- final Bundle in = new Bundle();
- in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
- client.call(DELETE_CONTRIBUTED_MEDIA_CALL, null, in);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ public static void deleteContributedMedia(Context context, String packageName,
+ UserHandle user) throws IOException {
+ final UserManager um = context.getSystemService(UserManager.class);
+ if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
+ try {
+ final ContentResolver resolver = context
+ .createPackageContextAsUser(packageName, 0, user).getContentResolver();
+ final Bundle in = new Bundle();
+ in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ resolver.call(AUTHORITY, DELETE_CONTRIBUTED_MEDIA_CALL, null, in);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ } else {
+ throw new IOException("User " + user + " must be unlocked and running");
}
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index de6afcbad9a6..93a59502ebea 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12852,6 +12852,13 @@ public final class Settings {
public static final String HIDDEN_API_POLICY = "hidden_api_policy";
/**
+ * Current version of signed configuration applied.
+ *
+ * @hide
+ */
+ public static final String SIGNED_CONFIG_VERSION = "signed_config_version";
+
+ /**
* Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
* operation (in ms).
*
@@ -13926,13 +13933,6 @@ public final class Settings {
/**
* Store a name/value pair into the database.
* <p>
- * The method takes an optional tag to associate with the setting which can be used to clear
- * only settings made by your package and associated with this tag by passing the tag to
- * {@link #resetToDefaults(ContentResolver, String)}. The value of this setting can be
- * overridden by future calls to this or other put methods, and the tag provided in those
- * calls, which may be null, will override the tag provided in this call. Any call to a put
- * method which does not accept a tag will effectively set the tag to null.
- * </p><p>
* Also the method takes an argument whether to make the value the default for this setting.
* If the system already specified a default value, then the one passed in here will
* <strong>not</strong> be set as the default.
@@ -13941,46 +13941,47 @@ public final class Settings {
* @param resolver to access the database with.
* @param name to store.
* @param value to associate with the name.
- * @param tag to associated with the setting.
* @param makeDefault whether to make the value the default one.
* @return true if the value was set, false on database errors.
*
- * @see #resetToDefaults(ContentResolver, String)
+ * @see #resetToDefaults(ContentResolver, int, String)
*
* @hide
*/
// TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- static boolean putString(@NonNull ContentResolver resolver,
- @NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault) {
- return sNameValueCache.putStringForUser(resolver, name, value, tag, makeDefault,
+ static boolean putString(@NonNull ContentResolver resolver, @NonNull String name,
+ @Nullable String value, boolean makeDefault) {
+ return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault,
resolver.getUserId());
}
/**
- * Reset the settings to their defaults. This would reset <strong>only</strong> settings set
- * by the caller's package. Passing in the optional tag will reset only settings changed by
- * your package and associated with this tag.
+ * Reset the values to their defaults.
+ * <p>
+ * The method accepts an optional prefix parameter. If provided, only pairs with a name that
+ * starts with the exact prefix will be reset. Otherwise all will be reset.
*
* @param resolver Handle to the content resolver.
- * @param tag Optional tag which should be associated with the settings to reset.
+ * @param resetMode The reset mode to use.
+ * @param prefix Optionally, to limit which which pairs are reset.
*
- * @see #putString(ContentResolver, String, String, String, boolean)
+ * @see #putString(ContentResolver, String, String, boolean)
*
* @hide
*/
// TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
- static void resetToDefaults(@NonNull ContentResolver resolver,
- @Nullable String tag) {
+ static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
+ @Nullable String prefix) {
try {
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
- if (tag != null) {
- arg.putString(CALL_METHOD_TAG_KEY, tag);
+ arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+ if (prefix != null) {
+ arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
}
- arg.putInt(CALL_METHOD_RESET_MODE_KEY, RESET_MODE_PACKAGE_DEFAULTS);
+ arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
CALL_METHOD_RESET_CONFIG, null, arg);
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1cd76d2e9ec9..261291741f96 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
@@ -35,6 +36,7 @@ import android.view.autofill.AutofillValue;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* A service that calculates field classification scores.
@@ -51,6 +53,7 @@ import java.util.List;
* {@hide}
*/
@SystemApi
+@TestApi
public abstract class AutofillFieldClassificationService extends Service {
private static final String TAG = "AutofillFieldClassificationService";
@@ -75,17 +78,32 @@ public abstract class AutofillFieldClassificationService extends Service {
public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
"android.autofill.field_classification.available_algorithms";
+ /**
+ * Field classification algorithm that computes the edit distance between two Strings.
+ *
+ * <p>Service implementation must provide this algorithm.</p>
+ */
+ public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
+
+ /**
+ * Field classification algorithm that computes whether the last four digits between two
+ * Strings match exactly.
+ *
+ * <p>Service implementation must provide this algorithm.</p>
+ */
+ public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
/** {@hide} **/
public static final String EXTRA_SCORES = "scores";
private AutofillFieldClassificationServiceWrapper mWrapper;
- private void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
- List<AutofillValue> actualValues, String[] userDataValues) {
+ private void calculateScores(RemoteCallback callback, List<AutofillValue> actualValues,
+ String[] userDataValues, String[] categoryIds, String defaultAlgorithm,
+ Bundle defaultArgs, Map algorithms, Map args) {
final Bundle data = new Bundle();
- final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
- Arrays.asList(userDataValues));
+ final float[][] scores = onCalculateScores(actualValues, Arrays.asList(userDataValues),
+ Arrays.asList(categoryIds), defaultAlgorithm, defaultArgs, algorithms, args);
if (scores != null) {
data.putParcelable(EXTRA_SCORES, new Scores(scores));
}
@@ -169,26 +187,111 @@ public abstract class AutofillFieldClassificationService extends Service {
* @return the calculated scores of {@code actualValues} x {@code userDataValues}.
*
* {@hide}
+ *
+ * @deprecated Use {@link AutofillFieldClassificationService#onCalculateScores} instead.
*/
@Nullable
@SystemApi
+ @Deprecated
public float[][] onGetScores(@Nullable String algorithm,
@Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
- Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+ Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScores()");
+ return null;
+ }
+
+ /**
+ * Calculates field classification scores in a batch.
+ *
+ * <p>A field classification score is a {@code float} representing how well an
+ * {@link AutofillValue} matches a expected value predicted by an autofill service
+ * &mdash;a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
+ *
+ * <p>The exact score depends on the algorithm used to calculate it&mdash;the service must
+ * provide at least one default algorithm (which is used when the algorithm is not specified
+ * or is invalid), but it could provide more (in which case the algorithm name should be
+ * specified by the caller when calculating the scores).
+ *
+ * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that
+ * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to:
+ *
+ * <pre>
+ * HashMap algorithms = new HashMap<>();
+ * algorithms.put("email", "EXACT_MATCH");
+ * algorithms.put("phone", "EXACT_MATCH");
+ *
+ * HashMap args = new HashMap<>();
+ * args.put("email", null);
+ * args.put("phone", null);
+ *
+ * service.onCalculateScores(Arrays.asList(AutofillValue.forText("email1"),
+ * AutofillValue.forText("PHONE1")), Arrays.asList("email1", "phone1"),
+ * Array.asList("email", "phone"), algorithms, args);
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 0.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * <p>If the same algorithm allows the caller to specify whether the comparisons should be
+ * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to:
+ *
+ * <pre>
+ * Bundle algorithmOptions = new Bundle();
+ * algorithmOptions.putBoolean("case_sensitive", false);
+ * args.put("phone", algorithmOptions);
+ *
+ * service.onCalculateScores(Arrays.asList(AutofillValue.forText("email1"),
+ * AutofillValue.forText("PHONE1")), Arrays.asList("email1", "phone1"),
+ * Array.asList("email", "phone"), algorithms, args);
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 1.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * @param actualValues values entered by the user.
+ * @param userDataValues values predicted from the user data.
+ * @param categoryIds category Ids correspoinding to userDataValues
+ * @param defaultAlgorithm default field classification algorithm
+ * @param algorithms array of field classification algorithms
+ * @return the calculated scores of {@code actualValues} x {@code userDataValues}.
+ *
+ * {@hide}
+ */
+ @Nullable
+ @SystemApi
+ public float[][] onCalculateScores(@NonNull List<AutofillValue> actualValues,
+ @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable Map algorithms, @Nullable Map args) {
+ Log.e(TAG, "service implementation (" + getClass()
+ + " does not implement onCalculateScore()");
return null;
}
private final class AutofillFieldClassificationServiceWrapper
extends IAutofillFieldClassificationService.Stub {
@Override
- public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
- List<AutofillValue> actualValues, String[] userDataValues)
- throws RemoteException {
+ public void calculateScores(RemoteCallback callback, List<AutofillValue> actualValues,
+ String[] userDataValues, String[] categoryIds, String defaultAlgorithm,
+ Bundle defaultArgs, Map algorithms, Map args)
+ throws RemoteException {
mHandler.sendMessage(obtainMessage(
- AutofillFieldClassificationService::getScores,
+ AutofillFieldClassificationService::calculateScores,
AutofillFieldClassificationService.this,
- callback, algorithmName, algorithmArgs, actualValues, userDataValues));
+ callback, actualValues, userDataValues, categoryIds, defaultAlgorithm,
+ defaultArgs, algorithms, args));
}
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7bf1f83f6bd8..d408e9a6c3b1 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -84,6 +84,7 @@ public final class FillResponse implements Parcelable {
private final @Nullable AutofillId[] mFieldClassificationIds;
private final int mFlags;
private int mRequestId;
+ private final @Nullable UserData mUserData;
private FillResponse(@NonNull Builder builder) {
mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -99,6 +100,7 @@ public final class FillResponse implements Parcelable {
mFieldClassificationIds = builder.mFieldClassificationIds;
mFlags = builder.mFlags;
mRequestId = INVALID_REQUEST_ID;
+ mUserData = builder.mUserData;
}
/** @hide */
@@ -157,6 +159,11 @@ public final class FillResponse implements Parcelable {
}
/** @hide */
+ public @Nullable UserData getUserData() {
+ return mUserData;
+ }
+
+ /** @hide */
@TestApi
public int getFlags() {
return mFlags;
@@ -198,6 +205,7 @@ public final class FillResponse implements Parcelable {
private AutofillId[] mFieldClassificationIds;
private int mFlags;
private boolean mDestroyed;
+ private UserData mUserData;
/**
* Triggers a custom UI before before autofilling the screen with any data set in this
@@ -506,6 +514,21 @@ public final class FillResponse implements Parcelable {
}
/**
+ * Sets a specific {@link UserData} for field classification for this request only.
+ *
+ * @return this builder
+ * @throws IllegalStateException if the FillResponse
+ * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+ * requires authentication}.
+ */
+ public Builder setUserData(@NonNull UserData userData) {
+ throwIfDestroyed();
+ throwIfAuthenticationCalled();
+ mUserData = Preconditions.checkNotNull(userData);
+ return this;
+ }
+
+ /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
@@ -599,6 +622,9 @@ public final class FillResponse implements Parcelable {
if (mFieldClassificationIds != null) {
builder.append(Arrays.toString(mFieldClassificationIds));
}
+ if (mUserData != null) {
+ builder.append(", userData=").append(mUserData);
+ }
return builder.append("]").toString();
}
@@ -621,6 +647,7 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelable(mHeader, flags);
parcel.writeParcelable(mFooter, flags);
+ parcel.writeParcelable(mUserData, flags);
parcel.writeParcelableArray(mIgnoredIds, flags);
parcel.writeLong(mDisableDuration);
parcel.writeParcelableArray(mFieldClassificationIds, flags);
@@ -661,6 +688,10 @@ public final class FillResponse implements Parcelable {
if (footer != null) {
builder.setFooter(footer);
}
+ final UserData userData = parcel.readParcelable(null);
+ if (userData != null) {
+ builder.setUserData(userData);
+ }
builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
final long disableDuration = parcel.readLong();
diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
index 398557d5ad2e..2cd24f96a22d 100644
--- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
+++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.os.RemoteCallback;
import android.view.autofill.AutofillValue;
import java.util.List;
+import java.util.Map;
/**
* Service used to calculate match scores for Autofill Field Classification.
@@ -27,6 +28,8 @@ import java.util.List;
* @hide
*/
oneway interface IAutofillFieldClassificationService {
- void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs,
- in List<AutofillValue> actualValues, in String[] userDataValues);
+ void calculateScores(in RemoteCallback callback, in List<AutofillValue> actualValues,
+ in String[] userDataValues, in String[] categoryIds,
+ in String defaultAlgorithm, in Bundle defaultArgs,
+ in Map algorithms, in Map args);
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index fccb85b957fa..37f192366f81 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -24,6 +24,7 @@ import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.os.Bundle;
@@ -32,6 +33,7 @@ import android.os.Parcelable;
import android.provider.Settings;
import android.service.autofill.FieldClassification.Match;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.autofill.AutofillManager;
@@ -57,28 +59,57 @@ public final class UserData implements Parcelable {
private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
private final String mId;
- private final String mAlgorithm;
- private final Bundle mAlgorithmArgs;
private final String[] mCategoryIds;
private final String[] mValues;
+ private final String mDefaultAlgorithm;
+ private final Bundle mDefaultArgs;
+ private final ArrayMap<String, String> mCategoryAlgorithms;
+ private final ArrayMap<String, Bundle> mCategoryArgs;
+
private UserData(Builder builder) {
mId = builder.mId;
- mAlgorithm = builder.mAlgorithm;
- mAlgorithmArgs = builder.mAlgorithmArgs;
mCategoryIds = new String[builder.mCategoryIds.size()];
builder.mCategoryIds.toArray(mCategoryIds);
mValues = new String[builder.mValues.size()];
builder.mValues.toArray(mValues);
+ builder.mValues.toArray(mValues);
+
+ mDefaultAlgorithm = builder.mDefaultAlgorithm;
+ mDefaultArgs = builder.mDefaultArgs;
+ mCategoryAlgorithms = builder.mCategoryAlgorithms;
+ mCategoryArgs = builder.mCategoryArgs;
}
/**
- * Gets the name of the algorithm that is used to calculate
- * {@link Match#getScore() match scores}.
+ * Gets the name of the default algorithm that is used to calculate
+ * {@link Match#getScore()} match scores}.
*/
@Nullable
public String getFieldClassificationAlgorithm() {
- return mAlgorithm;
+ return mDefaultAlgorithm;
+ }
+
+ /** @hide */
+ public Bundle getDefaultFieldClassificationArgs() {
+ return mDefaultArgs;
+ }
+
+ /**
+ * Gets the name of the algorithm corresponding to the specific autofill category
+ * that is used to calculate {@link Match#getScore() match scores}
+ *
+ * @param categoryId autofill field category
+ *
+ * @return String name of algorithm, null if none found.
+ */
+ @Nullable
+ public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
+ Preconditions.checkNotNull(categoryId);
+ if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
+ return null;
+ }
+ return mCategoryAlgorithms.get(categoryId);
}
/**
@@ -89,11 +120,6 @@ public final class UserData implements Parcelable {
}
/** @hide */
- public Bundle getAlgorithmArgs() {
- return mAlgorithmArgs;
- }
-
- /** @hide */
public String[] getCategoryIds() {
return mCategoryIds;
}
@@ -104,11 +130,29 @@ public final class UserData implements Parcelable {
}
/** @hide */
+ @TestApi
+ public ArrayMap<String, String> getFieldClassificationAlgorithms() {
+ return mCategoryAlgorithms;
+ }
+
+ /** @hide */
+ public ArrayMap<String, Bundle> getFieldClassificationArgs() {
+ return mCategoryArgs;
+ }
+
+ /** @hide */
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId);
- pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
- pw.print(" Args: "); pw.println(mAlgorithmArgs);
-
+ pw.print(prefix); pw.print("Default Algorithm: "); pw.print(mDefaultAlgorithm);
+ pw.print(prefix); pw.print("Default Args"); pw.print(mDefaultArgs);
+ if (mCategoryAlgorithms != null && mCategoryAlgorithms.size() > 0) {
+ pw.print(prefix); pw.print("Algorithms per category: ");
+ for (int i = 0; i < mCategoryAlgorithms.size(); i++) {
+ pw.print(prefix); pw.print(prefix); pw.print(mCategoryAlgorithms.keyAt(i));
+ pw.print(": "); pw.println(Helper.getRedacted(mCategoryAlgorithms.valueAt(i)));
+ pw.print("args="); pw.print(mCategoryArgs.get(mCategoryAlgorithms.keyAt(i)));
+ }
+ }
// Cannot disclose field ids or values because they could contain PII
pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
for (int i = 0; i < mCategoryIds.length; i++) {
@@ -139,8 +183,13 @@ public final class UserData implements Parcelable {
private final String mId;
private final ArrayList<String> mCategoryIds;
private final ArrayList<String> mValues;
- private String mAlgorithm;
- private Bundle mAlgorithmArgs;
+ private String mDefaultAlgorithm;
+ private Bundle mDefaultArgs;
+
+ // Map of autofill field categories to fleid classification algorithms and args
+ private ArrayMap<String, String> mCategoryAlgorithms;
+ private ArrayMap<String, Bundle> mCategoryArgs;
+
private boolean mDestroyed;
// Non-persistent array used to limit the number of unique ids.
@@ -148,7 +197,6 @@ public final class UserData implements Parcelable {
// Non-persistent array used to ignore duplaicated value/category pairs.
private final ArraySet<String> mUniqueValueCategoryPairs;
-
/**
* Creates a new builder for the user data used for <a href="#FieldClassification">field
* classification</a>.
@@ -169,7 +217,7 @@ public final class UserData implements Parcelable {
* {@link AutofillManager#getUserData()}).
*
* @param value value of the user data.
- * @param categoryId string used to identify the category the value is associated with.
+ * @param categoryId autofill field category.
*
* @throws IllegalArgumentException if any of the following occurs:
* <ul>
@@ -189,13 +237,15 @@ public final class UserData implements Parcelable {
mCategoryIds = new ArrayList<>(maxUserDataSize);
mValues = new ArrayList<>(maxUserDataSize);
mUniqueValueCategoryPairs = new ArraySet<>(maxUserDataSize);
+
mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
addMapping(value, categoryId);
}
/**
- * Sets the algorithm used for <a href="#FieldClassification">field classification</a>.
+ * Sets the default algorithm used for
+ * <a href="#FieldClassification">field classification</a>.
*
* <p>The currently available algorithms can be retrieve through
* {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
@@ -212,8 +262,40 @@ public final class UserData implements Parcelable {
public Builder setFieldClassificationAlgorithm(@Nullable String name,
@Nullable Bundle args) {
throwIfDestroyed();
- mAlgorithm = name;
- mAlgorithmArgs = args;
+ mDefaultAlgorithm = name;
+ mDefaultArgs = args;
+ return this;
+ }
+
+ /**
+ * Sets the algorithm used for <a href="#FieldClassification">field classification</a>
+ * for the specified category.
+ *
+ * <p>The currently available algorithms can be retrieved through
+ * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
+ *
+ * <p>If not set, the
+ * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
+ * used instead.
+ *
+ * @param categoryId autofill field category.
+ * @param name name of the algorithm or {@code null} to used default.
+ * @param args optional arguments to the algorithm.
+ *
+ * @return this builder
+ */
+ public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId,
+ @Nullable String name, @Nullable Bundle args) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(categoryId);
+ if (mCategoryAlgorithms == null) {
+ mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount());
+ }
+ if (mCategoryArgs == null) {
+ mCategoryArgs = new ArrayMap<>(getMaxCategoryCount());
+ }
+ mCategoryAlgorithms.put(categoryId, name);
+ mCategoryArgs.put(categoryId, args);
return this;
}
@@ -317,8 +399,7 @@ public final class UserData implements Parcelable {
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
- .append(", algorithm=").append(mAlgorithm);
+ final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId);
// Cannot disclose category ids or values because they could contain PII
builder.append(", categoryIds=");
Helper.appendRedacted(builder, mCategoryIds);
@@ -341,8 +422,10 @@ public final class UserData implements Parcelable {
parcel.writeString(mId);
parcel.writeStringArray(mCategoryIds);
parcel.writeStringArray(mValues);
- parcel.writeString(mAlgorithm);
- parcel.writeBundle(mAlgorithmArgs);
+ parcel.writeString(mDefaultAlgorithm);
+ parcel.writeBundle(mDefaultArgs);
+ parcel.writeMap(mCategoryAlgorithms);
+ parcel.writeMap(mCategoryArgs);
}
public static final Parcelable.Creator<UserData> CREATOR =
@@ -355,10 +438,28 @@ public final class UserData implements Parcelable {
final String id = parcel.readString();
final String[] categoryIds = parcel.readStringArray();
final String[] values = parcel.readStringArray();
+ final String defaultAlgorithm = parcel.readString();
+ final Bundle defaultArgs = parcel.readBundle();
+ final ArrayMap<String, String> categoryAlgorithms = new ArrayMap<>();
+ parcel.readMap(categoryAlgorithms, String.class.getClassLoader());
+ final ArrayMap<String, Bundle> categoryArgs = new ArrayMap<>();
+ parcel.readMap(categoryArgs, Bundle.class.getClassLoader());
+
final Builder builder = new Builder(id, values[0], categoryIds[0])
- .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
+ .setFieldClassificationAlgorithm(defaultAlgorithm, defaultArgs);
+
for (int i = 1; i < categoryIds.length; i++) {
- builder.add(values[i], categoryIds[i]);
+ String categoryId = categoryIds[i];
+ builder.add(values[i], categoryId);
+ }
+
+ final int size = categoryAlgorithms.size();
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ final String categoryId = categoryAlgorithms.keyAt(i);
+ builder.setFieldClassificationAlgorithmForCategory(categoryId,
+ categoryAlgorithms.valueAt(i), categoryArgs.get(categoryId));
+ }
}
return builder.build();
}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 68a86f3c8bca..9aff281b0472 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -32,7 +32,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -172,7 +171,9 @@ public abstract class AugmentedAutofillService extends Service {
mAutofillProxies.put(sessionId, proxy);
} else {
// TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+ // TODO(b/111330312): also make sure to cover scenario on CTS test
if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ proxy.update(focusedId, focusedValue);
}
// TODO(b/111330312): set cancellation signal
final CancellationSignal cancellationSignal = null;
@@ -248,8 +249,10 @@ public abstract class AugmentedAutofillService extends Service {
private final IFillCallback mCallback;
public final int taskId;
public final ComponentName componentName;
- public final AutofillId focusedId;
- public final AutofillValue focusedValue;
+ @GuardedBy("mLock")
+ private AutofillId mFocusedId;
+ @GuardedBy("mLock")
+ private AutofillValue mFocusedValue;
// Objects used to log metrics
private final long mRequestTime;
@@ -272,8 +275,8 @@ public abstract class AugmentedAutofillService extends Service {
mCallback = callback;
this.taskId = taskId;
this.componentName = componentName;
- this.focusedId = focusedId;
- this.focusedValue = focusedValue;
+ this.mFocusedId = focusedId;
+ this.mFocusedValue = focusedValue;
this.mRequestTime = requestTime;
// TODO(b/111330312): linkToDeath
}
@@ -286,13 +289,13 @@ public abstract class AugmentedAutofillService extends Service {
}
Rect rect;
try {
- rect = mClient.getViewCoordinates(focusedId);
+ rect = mClient.getViewCoordinates(mFocusedId);
} catch (RemoteException e) {
- Log.w(TAG, "Could not get coordinates for " + focusedId);
+ Log.w(TAG, "Could not get coordinates for " + mFocusedId);
return null;
}
if (rect == null) {
- if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
+ if (DEBUG) Log.d(TAG, "getViewCoordinates(" + mFocusedId + ") returned null");
return null;
}
mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
@@ -325,6 +328,28 @@ public abstract class AugmentedAutofillService extends Service {
}
}
+ private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
+ synchronized (mLock) {
+ // TODO(b/111330312): should we close the popupwindow if the focused id changed?
+ mFocusedId = focusedId;
+ mFocusedValue = focusedValue;
+ }
+ }
+
+ @NonNull
+ public AutofillId getFocusedId() {
+ synchronized (mLock) {
+ return mFocusedId;
+ }
+ }
+
+ @NonNull
+ public AutofillValue getFocusedValue() {
+ synchronized (mLock) {
+ return mFocusedValue;
+ }
+ }
+
// Used (mostly) for metrics.
public void report(@ReportEvent int event) {
switch (event) {
@@ -372,9 +397,9 @@ public abstract class AugmentedAutofillService extends Service {
pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
pw.print(prefix); pw.print("component: ");
pw.println(componentName.flattenToShortString());
- pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
- if (focusedValue != null) {
- pw.print(prefix); pw.print("focusedValue: "); pw.println(focusedValue);
+ pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
+ if (mFocusedValue != null) {
+ pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
}
pw.print(prefix); pw.print("client: "); pw.println(mClient);
final String prefix2 = prefix + " ";
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 054646572a58..620ec59c9e55 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -15,10 +15,13 @@
*/
package android.service.autofill.augmented;
+import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
+import android.util.Log;
/**
* Callback used to indicate at {@link FillRequest} has been fulfilled.
@@ -27,6 +30,9 @@ import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy
*/
@SystemApi
public final class FillCallback {
+
+ private static final String TAG = FillCallback.class.getSimpleName();
+
private final AutofillProxy mProxy;
FillCallback(@NonNull AutofillProxy proxy) {
@@ -40,7 +46,11 @@ public final class FillCallback {
* could not provide autofill for the request.
*/
public void onSuccess(@Nullable FillResponse response) {
+ if (DEBUG) Log.d(TAG, "onSuccess(): " + response);
+
mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS);
+ if (response == null) return;
+
final FillWindow fillWindow = response.getFillWindow();
if (fillWindow != null) {
fillWindow.show();
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index fd75b156858e..57d2cc85f7ea 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -58,7 +58,7 @@ public final class FillRequest {
*/
@NonNull
public AutofillId getFocusedId() {
- return mProxy.focusedId;
+ return mProxy.getFocusedId();
}
/**
@@ -66,7 +66,7 @@ public final class FillRequest {
*/
@NonNull
public AutofillValue getFocusedValue() {
- return mProxy.focusedValue;
+ return mProxy.getFocusedValue();
}
/**
@@ -82,6 +82,6 @@ public final class FillRequest {
@Override
public String toString() {
return "FillRequest[act=" + getActivityComponent().flattenToShortString()
- + ", id=" + mProxy.focusedId + "]";
+ + ", id=" + mProxy.getFocusedId() + "]";
}
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 7064b6fe8079..1ecfab4edc12 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -102,6 +102,8 @@ public final class FillResponse implements Parcelable {
// TODO(b/111330312): add methods to disable app / activity, either here or on manager
}
+ // TODO(b/111330312): implement to String
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 518f8ed11a26..de532b74375f 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -80,11 +80,18 @@ public final class Adjustment implements Parcelable {
* Data type: int, one of importance values e.g.
* {@link android.app.NotificationManager#IMPORTANCE_MIN}.
*
- * If used from
- * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, it can
- * block a notification from appearing or silence it. If used from
- * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can visually
- * demote a notification.
+ * <p> If used from
+ * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, and
+ * received before the notification is posted, it can block a notification from appearing or
+ * silence it. Importance adjustments received too late from
+ * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)} will be
+ * ignored.
+ * </p>
+ * <p>If used from
+ * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can
+ * visually demote or cancel a notification, but use this with care if they notification was
+ * recently posted because the notification may already have made noise.
+ * </p>
*/
public static final String KEY_IMPORTANCE = "key_importance";
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index dee6d908c4c5..ade7577dc666 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,12 +42,15 @@ public class FeatureFlagUtils {
static {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
- DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "true");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
+ DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+ DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+ DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
+ DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
+ DEFAULT_FLAGS.put("settings_wifi_sharing", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
- DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
DEFAULT_FLAGS.put(SAFETY_HUB, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index ba5340c826d3..fb4f9c03fa68 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -38,9 +38,14 @@ public class InsetsController implements WindowInsetsController {
private final InsetsState mState = new InsetsState();
private final Rect mFrame = new Rect();
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+ private final ViewRootImpl mViewRoot;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+ public InsetsController(ViewRootImpl viewRoot) {
+ mViewRoot = viewRoot;
+ }
+
void onFrameChanged(Rect frame) {
mFrame.set(frame);
}
@@ -49,8 +54,14 @@ public class InsetsController implements WindowInsetsController {
return mState;
}
- public void setState(InsetsState state) {
+ boolean onStateChanged(InsetsState state) {
+ if (mState.equals(state)) {
+ return false;
+ }
mState.set(state);
+ applyLocalVisibilityOverride();
+ mViewRoot.notifyInsetsChanged();
+ return true;
}
/**
@@ -105,17 +116,28 @@ public class InsetsController implements WindowInsetsController {
}
}
+ private void applyLocalVisibilityOverride() {
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
+ controller.applyLocalVisibilityOverride();
+ }
+ }
+
@VisibleForTesting
public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
InsetsSourceConsumer controller = mSourceConsumers.get(type);
if (controller != null) {
return controller;
}
- controller = new InsetsSourceConsumer(type, mState, Transaction::new);
+ controller = new InsetsSourceConsumer(type, mState, Transaction::new, this);
mSourceConsumers.put(type, controller);
return controller;
}
+ void notifyVisibilityChanged() {
+ mViewRoot.notifyInsetsChanged();
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.println(prefix); pw.println("InsetsController:");
mState.dump(prefix + " ", pw);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index e74aa8dfcf4e..ec85c4c56cfc 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -33,27 +33,31 @@ public class InsetsSourceConsumer {
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetType int mType;
private final InsetsState mState;
- private @Nullable InsetsSourceControl mControl;
+ private final InsetsController mController;
+ private @Nullable InsetsSourceControl mSourceControl;
private boolean mHidden;
public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
- Supplier<Transaction> transactionSupplier) {
+ Supplier<Transaction> transactionSupplier, InsetsController controller) {
mType = type;
mState = state;
mTransactionSupplier = transactionSupplier;
+ mController = controller;
}
public void setControl(@Nullable InsetsSourceControl control) {
- if (mControl == control) {
+ if (mSourceControl == control) {
return;
}
- mControl = control;
+ mSourceControl = control;
applyHiddenToControl();
+ applyLocalVisibilityOverride();
+ mController.notifyVisibilityChanged();
}
@VisibleForTesting
public InsetsSourceControl getControl() {
- return mControl;
+ return mSourceControl;
}
int getType() {
@@ -70,25 +74,36 @@ public class InsetsSourceConsumer {
setHidden(true);
}
+ void applyLocalVisibilityOverride() {
+
+ // If we don't have control, we are not able to change the visibility.
+ if (mSourceControl == null) {
+ return;
+ }
+ mState.getSource(mType).setVisible(!mHidden);
+ }
+
private void setHidden(boolean hidden) {
if (mHidden == hidden) {
return;
}
mHidden = hidden;
applyHiddenToControl();
+ applyLocalVisibilityOverride();
+ mController.notifyVisibilityChanged();
}
private void applyHiddenToControl() {
- if (mControl == null) {
+ if (mSourceControl == null) {
return;
}
// TODO: Animation
final Transaction t = mTransactionSupplier.get();
if (mHidden) {
- t.hide(mControl.getLeash());
+ t.hide(mSourceControl.getLeash());
} else {
- t.show(mControl.getLeash());
+ t.show(mSourceControl.getLeash());
}
t.apply();
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index bf1a005bad18..34d076fba54d 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -299,6 +299,8 @@ public final class ThreadedRenderer extends HardwareRenderer {
private boolean mEnabled;
private boolean mRequested = true;
+ private FrameDrawingCallback mNextRtFrameCallback;
+
ThreadedRenderer(Context context, boolean translucent, String name) {
super();
setName(name);
@@ -432,6 +434,17 @@ public final class ThreadedRenderer extends HardwareRenderer {
}
/**
+ * Registers a callback to be executed when the next frame is being drawn on RenderThread. This
+ * callback will be executed on a RenderThread worker thread, and only used for the next frame
+ * and thus it will only fire once.
+ *
+ * @param callback The callback to register.
+ */
+ void registerRtFrameCallback(FrameDrawingCallback callback) {
+ mNextRtFrameCallback = callback;
+ }
+
+ /**
* Destroys all hardware rendering resources associated with the specified
* view hierarchy.
*
@@ -562,6 +575,15 @@ public final class ThreadedRenderer extends HardwareRenderer {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
updateViewTreeDisplayList(view);
+ // Consume and set the frame callback after we dispatch draw to the view above, but before
+ // onPostDraw below which may reset the callback for the next frame. This ensures that
+ // updates to the frame callback during scroll handling will also apply in this frame.
+ final FrameDrawingCallback callback = mNextRtFrameCallback;
+ mNextRtFrameCallback = null;
+ if (callback != null) {
+ setFrameCallback(callback);
+ }
+
if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
RecordingCanvas canvas = mRootNode.startRecording(mSurfaceWidth, mSurfaceHeight);
try {
@@ -619,10 +641,8 @@ public final class ThreadedRenderer extends HardwareRenderer {
*
* @param view The view to draw.
* @param attachInfo AttachInfo tied to the specified view.
- * @param callbacks Callbacks invoked when drawing happens.
*/
- void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks,
- FrameDrawingCallback frameDrawingCallback) {
+ void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
@@ -642,9 +662,6 @@ public final class ThreadedRenderer extends HardwareRenderer {
attachInfo.mPendingAnimatingRenderNodes = null;
}
- if (frameDrawingCallback != null) {
- setFrameCallback(frameDrawingCallback);
- }
int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cb4788624935..e36e2588f3d7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -200,8 +200,6 @@ public final class ViewRootImpl implements ViewParent,
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
static boolean sFirstDrawComplete = false;
- private FrameDrawingCallback mNextRtFrameCallback;
-
/**
* Callback for notifying about global configuration changes.
*/
@@ -464,7 +462,7 @@ public final class ViewRootImpl implements ViewParent,
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
- private InsetsState mPendingInsets = new InsetsState();
+ private InsetsState mTempInsets = new InsetsState();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -552,7 +550,7 @@ public final class ViewRootImpl implements ViewParent,
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
- private final InsetsController mInsetsController = new InsetsController();
+ private final InsetsController mInsetsController = new InsetsController(this);
static final class SystemUiVisibilityInfo {
int seq;
@@ -823,7 +821,7 @@ public final class ViewRootImpl implements ViewParent,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
- mInsetsController.getState());
+ mTempInsets);
setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
@@ -851,7 +849,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
- mPendingInsets = mInsetsController.getState();
+ mInsetsController.onStateChanged(mTempInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1052,7 +1050,9 @@ public final class ViewRootImpl implements ViewParent,
* @param callback The callback to register.
*/
public void registerRtFrameCallback(FrameDrawingCallback callback) {
- mNextRtFrameCallback = callback;
+ if (mAttachInfo.mThreadedRenderer != null) {
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(callback);
+ }
}
@UnsupportedAppUsage
@@ -1342,6 +1342,19 @@ public final class ViewRootImpl implements ViewParent,
scheduleTraversals();
}
+ void notifyInsetsChanged() {
+ if (!USE_NEW_INSETS) {
+ return;
+ }
+ mApplyInsetsRequested = true;
+
+ // If this changes during traversal, no need to schedule another one as it will dispatch it
+ // during the current traversal.
+ if (!mIsInTraversal) {
+ scheduleTraversals();
+ }
+ }
+
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
@@ -2027,9 +2040,6 @@ public final class ViewRootImpl implements ViewParent,
if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
insetsChanged = true;
}
- if (!mPendingInsets.equals(mInsetsController.getState())) {
- insetsChanged = true;
- }
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
@@ -2223,8 +2233,6 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mStableInsets);
final boolean cutoutChanged = !mPendingDisplayCutout.equals(
mAttachInfo.mDisplayCutout);
- final boolean insetsStateChanged = !mPendingInsets.equals(
- mInsetsController.getState());
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
final boolean surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -2262,10 +2270,6 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
- if (insetsStateChanged) {
- mInsetsController.setState(mPendingInsets);
- contentInsetsChanged = true;
- }
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -3534,10 +3538,7 @@ public final class ViewRootImpl implements ViewParent,
useAsyncReport = true;
- // draw(...) might invoke post-draw, which might register the next callback already.
- final FrameDrawingCallback callback = mNextRtFrameCallback;
- mNextRtFrameCallback = null;
- mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
+ mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
@@ -4378,22 +4379,12 @@ public final class ViewRootImpl implements ViewParent,
}
break;
case MSG_INSETS_CHANGED:
- mPendingInsets = (InsetsState) msg.obj;
-
- // TODO: Full traversal not needed here.
- if (USE_NEW_INSETS) {
- requestLayout();
- }
+ mInsetsController.onStateChanged((InsetsState) msg.obj);
break;
case MSG_INSETS_CONTROL_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
- mPendingInsets = (InsetsState) args.arg1;
mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
-
- // TODO: Full traversal not necessarily needed here.
- if (USE_NEW_INSETS) {
- requestLayout();
- }
+ mInsetsController.onStateChanged((InsetsState) args.arg1);
break;
}
case MSG_WINDOW_MOVED:
@@ -6794,7 +6785,7 @@ public final class ViewRootImpl implements ViewParent,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurface, mPendingInsets);
+ mPendingMergedConfiguration, mSurface, mTempInsets);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6811,7 +6802,7 @@ public final class ViewRootImpl implements ViewParent,
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
setFrame(mTmpFrame);
-
+ mInsetsController.onStateChanged(mTempInsets);
return relayoutResult;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 48831daf75e8..1889692ea831 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -422,7 +422,7 @@ public final class ContentCaptureManager {
/** @hide */
public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.println("IntelligenceManager");
+ pw.print(prefix); pw.println("ContentCaptureManager");
final String prefix2 = prefix + " ";
pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 1a7b91127a7b..04b94b0e03ad 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -30,6 +30,7 @@ import android.os.Parcelable;
import android.text.SpannedString;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -654,6 +655,8 @@ public final class ConversationActions implements Parcelable {
@NonNull
@Hint
private final List<String> mHints;
+ @Nullable
+ private String mCallingPackageName;
private Request(
@NonNull List<Message> conversation,
@@ -666,15 +669,26 @@ public final class ConversationActions implements Parcelable {
mHints = hints;
}
- private Request(Parcel in) {
+ private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
in.readParcelableList(conversation, null);
- mConversation = Collections.unmodifiableList(conversation);
- mTypeConfig = in.readParcelable(null);
- mMaxSuggestions = in.readInt();
+
+ TypeConfig typeConfig = in.readParcelable(null);
+
+ int maxSuggestions = in.readInt();
+
List<String> hints = new ArrayList<>();
in.readStringList(hints);
- mHints = Collections.unmodifiableList(hints);
+
+ String callingPackageName = in.readString();
+
+ Request request = new Request(
+ conversation,
+ typeConfig,
+ maxSuggestions,
+ hints);
+ request.setCallingPackageName(callingPackageName);
+ return request;
}
@Override
@@ -683,6 +697,7 @@ public final class ConversationActions implements Parcelable {
parcel.writeParcelable(mTypeConfig, flags);
parcel.writeInt(mMaxSuggestions);
parcel.writeStringList(mHints);
+ parcel.writeString(mCallingPackageName);
}
@Override
@@ -694,7 +709,7 @@ public final class ConversationActions implements Parcelable {
new Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -730,6 +745,26 @@ public final class ConversationActions implements Parcelable {
return mHints;
}
+ /**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
/** Builder object to construct the {@link Request} object. */
public static final class Builder {
@NonNull
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index f8fce62c84d2..c24489c8740b 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -60,7 +60,7 @@ public final class SystemTextClassifier implements TextClassifier {
mSettings = Preconditions.checkNotNull(settings);
mFallback = context.getSystemService(TextClassificationManager.class)
.getTextClassifier(TextClassifier.LOCAL);
- mPackageName = Preconditions.checkNotNull(context.getPackageName());
+ mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
}
/**
@@ -72,6 +72,7 @@ public final class SystemTextClassifier implements TextClassifier {
Preconditions.checkNotNull(request);
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextSelectionCallback callback = new TextSelectionCallback();
mManagerService.onSuggestSelection(mSessionId, request, callback);
final TextSelection selection = callback.mReceiver.get();
@@ -93,6 +94,7 @@ public final class SystemTextClassifier implements TextClassifier {
Preconditions.checkNotNull(request);
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextClassificationCallback callback = new TextClassificationCallback();
mManagerService.onClassifyText(mSessionId, request, callback);
final TextClassification classification = callback.mReceiver.get();
@@ -150,6 +152,7 @@ public final class SystemTextClassifier implements TextClassifier {
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final TextLanguageCallback callback = new TextLanguageCallback();
mManagerService.onDetectLanguage(mSessionId, request, callback);
final TextLanguage textLanguage = callback.mReceiver.get();
@@ -168,6 +171,7 @@ public final class SystemTextClassifier implements TextClassifier {
Utils.checkMainThread();
try {
+ request.setCallingPackageName(mPackageName);
final ConversationActionsCallback callback = new ConversationActionsCallback();
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
final ConversationActions conversationActions = callback.mReceiver.get();
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index e0910c0b37b7..d9f79655d588 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -37,11 +37,13 @@ import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -518,6 +520,7 @@ public final class TextClassification implements Parcelable {
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final ZonedDateTime mReferenceTime;
@NonNull private final Bundle mExtras;
+ @Nullable private String mCallingPackageName;
private Request(
CharSequence text,
@@ -578,6 +581,26 @@ public final class TextClassification implements Parcelable {
}
/**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * For SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -660,7 +683,8 @@ public final class TextClassification implements Parcelable {
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+ return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
+ mDefaultLocales, mReferenceTime,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -672,25 +696,37 @@ public final class TextClassification implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText.toString());
+ dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
- dest.writeInt(mReferenceTime != null ? 1 : 0);
- if (mReferenceTime != null) {
- dest.writeString(mReferenceTime.toString());
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+ dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final CharSequence text = in.readCharSequence();
+ final int startIndex = in.readInt();
+ final int endIndex = in.readInt();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final String referenceTimeString = in.readString();
+ final ZonedDateTime referenceTime = referenceTimeString == null
+ ? null : ZonedDateTime.parse(referenceTimeString);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, startIndex, endIndex,
+ defaultLocales, referenceTime, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -698,15 +734,6 @@ public final class TextClassification implements Parcelable {
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mStartIndex = in.readInt();
- mEndIndex = in.readInt();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
- mExtras = in.readBundle();
- }
}
@Override
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index d28459e733f0..b1609fcfe56a 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -26,6 +26,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Locale;
@@ -90,7 +91,7 @@ public final class TextLanguage implements Parcelable {
* confidence to low confidence.
*
* @throws IndexOutOfBoundsException if the specified index is out of range.
- * @see #getLocaleCount() for the number of locales available.
+ * @see #getLocaleHypothesisCount() for the number of locales available.
*/
@NonNull
public ULocale getLocale(int index) {
@@ -222,11 +223,12 @@ public final class TextLanguage implements Parcelable {
};
private final CharSequence mText;
- private final Bundle mBundle;
+ private final Bundle mExtra;
+ @Nullable private String mCallingPackageName;
private Request(CharSequence text, Bundle bundle) {
mText = text;
- mBundle = bundle;
+ mExtra = bundle;
}
/**
@@ -238,6 +240,25 @@ public final class TextLanguage implements Parcelable {
}
/**
+ * Sets the name of the package that is sending this request.
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns null if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns a bundle containing non-structured extra information about this request.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -246,7 +267,7 @@ public final class TextLanguage implements Parcelable {
*/
@NonNull
public Bundle getExtras() {
- return mBundle.deepCopy();
+ return mExtra.deepCopy();
}
@Override
@@ -257,13 +278,18 @@ public final class TextLanguage implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mText);
- dest.writeBundle(mBundle);
+ dest.writeString(mCallingPackageName);
+ dest.writeBundle(mExtra);
}
private static Request readFromParcel(Parcel in) {
- return new Request(
- in.readCharSequence(),
- in.readBundle());
+ final CharSequence text = in.readCharSequence();
+ final String callingPackageName = in.readString();
+ final Bundle extra = in.readBundle();
+
+ final Request request = new Request(text, extra);
+ request.setCallingPackageName(callingPackageName);
+ return request;
}
/**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1e42c414a365..ab341782ac58 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -30,6 +30,7 @@ import android.text.method.MovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityConfig;
import android.view.textclassifier.TextClassifier.EntityType;
import android.widget.TextView;
@@ -205,27 +206,32 @@ public final class TextLinks implements Parcelable {
private final EntityConfidence mEntityScores;
private final int mStart;
private final int mEnd;
- @Nullable final URLSpan mUrlSpan;
+ private final Bundle mExtras;
+ @Nullable private final URLSpan mUrlSpan;
/**
* Create a new TextLink.
*
* @param start The start index of the identified subsequence
* @param end The end index of the identified subsequence
- * @param entityScores A mapping of entity type to confidence score
+ * @param entityConfidence A mapping of entity type to confidence score
+ * @param extras A bundle containing custom data related to this TextLink
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
*
- * @throws IllegalArgumentException if entityScores is null or empty
+ * @throws IllegalArgumentException if {@code entityConfidence} is null or empty
+ * @throws IllegalArgumentException if {@code start} is greater than {@code end}
*/
- TextLink(int start, int end, Map<String, Float> entityScores,
- @Nullable URLSpan urlSpan) {
- Preconditions.checkNotNull(entityScores);
- Preconditions.checkArgument(!entityScores.isEmpty());
+ private TextLink(int start, int end, @NonNull EntityConfidence entityConfidence,
+ @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+ Preconditions.checkNotNull(entityConfidence);
+ Preconditions.checkArgument(!entityConfidence.getEntities().isEmpty());
Preconditions.checkArgument(start <= end);
+ Preconditions.checkNotNull(extras);
mStart = start;
mEnd = end;
- mEntityScores = new EntityConfidence(entityScores);
+ mEntityScores = entityConfidence;
mUrlSpan = urlSpan;
+ mExtras = extras;
}
/**
@@ -274,6 +280,13 @@ public final class TextLinks implements Parcelable {
return mEntityScores.getConfidenceScore(entityType);
}
+ /**
+ * Returns a bundle containing custom data related to this TextLink.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public String toString() {
return String.format(Locale.US,
@@ -291,13 +304,22 @@ public final class TextLinks implements Parcelable {
mEntityScores.writeToParcel(dest, flags);
dest.writeInt(mStart);
dest.writeInt(mEnd);
+ dest.writeBundle(mExtras);
+ }
+
+ private static TextLink readFromParcel(Parcel in) {
+ final EntityConfidence entityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ final int start = in.readInt();
+ final int end = in.readInt();
+ final Bundle extras = in.readBundle();
+ return new TextLink(start, end, entityConfidence, extras, null /* urlSpan */);
}
public static final Parcelable.Creator<TextLink> CREATOR =
new Parcelable.Creator<TextLink>() {
@Override
public TextLink createFromParcel(Parcel in) {
- return new TextLink(in);
+ return readFromParcel(in);
}
@Override
@@ -305,13 +327,6 @@ public final class TextLinks implements Parcelable {
return new TextLink[size];
}
};
-
- private TextLink(Parcel in) {
- mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
- mStart = in.readInt();
- mEnd = in.readInt();
- mUrlSpan = null;
- }
}
/**
@@ -321,23 +336,21 @@ public final class TextLinks implements Parcelable {
private final CharSequence mText;
@Nullable private final LocaleList mDefaultLocales;
- @Nullable private final TextClassifier.EntityConfig mEntityConfig;
+ @Nullable private final EntityConfig mEntityConfig;
private final boolean mLegacyFallback;
- private String mCallingPackageName;
+ @Nullable private String mCallingPackageName;
private final Bundle mExtras;
private Request(
CharSequence text,
LocaleList defaultLocales,
- TextClassifier.EntityConfig entityConfig,
+ EntityConfig entityConfig,
boolean legacyFallback,
- String callingPackageName,
Bundle extras) {
mText = text;
mDefaultLocales = defaultLocales;
mEntityConfig = entityConfig;
mLegacyFallback = legacyFallback;
- mCallingPackageName = callingPackageName;
mExtras = extras;
}
@@ -360,10 +373,10 @@ public final class TextLinks implements Parcelable {
/**
* @return The config representing the set of entities to look for
- * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
+ * @see Builder#setEntityConfig(EntityConfig)
*/
@Nullable
- public TextClassifier.EntityConfig getEntityConfig() {
+ public EntityConfig getEntityConfig() {
return mEntityConfig;
}
@@ -378,13 +391,26 @@ public final class TextLinks implements Parcelable {
}
/**
- * Sets the name of the package that requested the links to get generated.
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
*/
- void setCallingPackageName(@Nullable String callingPackageName) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
mCallingPackageName = callingPackageName;
}
/**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -404,9 +430,8 @@ public final class TextLinks implements Parcelable {
private final CharSequence mText;
@Nullable private LocaleList mDefaultLocales;
- @Nullable private TextClassifier.EntityConfig mEntityConfig;
+ @Nullable private EntityConfig mEntityConfig;
private boolean mLegacyFallback = true; // Use legacy fall back by default.
- private String mCallingPackageName;
@Nullable private Bundle mExtras;
public Builder(@NonNull CharSequence text) {
@@ -434,7 +459,7 @@ public final class TextLinks implements Parcelable {
* @return this builder
*/
@NonNull
- public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+ public Builder setEntityConfig(@Nullable EntityConfig entityConfig) {
mEntityConfig = entityConfig;
return this;
}
@@ -455,18 +480,6 @@ public final class TextLinks implements Parcelable {
}
/**
- * Sets the name of the package that requested the links to get generated.
- *
- * @return this builder
- * @hide
- */
- @NonNull
- public Builder setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- return this;
- }
-
- /**
* Sets the extended data.
*
* @return this builder
@@ -483,21 +496,11 @@ public final class TextLinks implements Parcelable {
public Request build() {
return new Request(
mText, mDefaultLocales, mEntityConfig,
- mLegacyFallback, mCallingPackageName,
+ mLegacyFallback,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
- /**
- * @return the name of the package that requested the links to get generated.
- * TODO: make available as system API
- * @hide
- */
- @Nullable
- public String getCallingPackageName() {
- return mCallingPackageName;
- }
-
@Override
public int describeContents() {
return 0;
@@ -506,23 +509,30 @@ public final class TextLinks implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mText.toString());
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
- dest.writeInt(mEntityConfig != null ? 1 : 0);
- if (mEntityConfig != null) {
- mEntityConfig.writeToParcel(dest, flags);
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeParcelable(mEntityConfig, flags);
dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final String text = in.readString();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final EntityConfig entityConfig = in.readParcelable(null);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, defaultLocales, entityConfig,
+ /* legacyFallback= */ true, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -530,16 +540,6 @@ public final class TextLinks implements Parcelable {
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mEntityConfig = in.readInt() == 0
- ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
- mLegacyFallback = true;
- mCallingPackageName = in.readString();
- mExtras = in.readBundle();
- }
}
/**
@@ -645,9 +645,20 @@ public final class TextLinks implements Parcelable {
* @throws IllegalArgumentException if entityScores is null or empty.
*/
@NonNull
- public Builder addLink(int start, int end, Map<String, Float> entityScores) {
- mLinks.add(new TextLink(start, end, entityScores, null));
- return this;
+ public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores) {
+ return addLink(start, end, entityScores, Bundle.EMPTY, null);
+ }
+
+ /**
+ * Adds a TextLink.
+ *
+ * @see #addLink(int, int, Map)
+ * @param extras An optional bundle containing custom data related to this TextLink
+ */
+ @NonNull
+ public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+ @NonNull Bundle extras) {
+ return addLink(start, end, entityScores, extras, null);
}
/**
@@ -655,9 +666,15 @@ public final class TextLinks implements Parcelable {
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
*/
@NonNull
- Builder addLink(int start, int end, Map<String, Float> entityScores,
+ Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
@Nullable URLSpan urlSpan) {
- mLinks.add(new TextLink(start, end, entityScores, urlSpan));
+ return addLink(start, end, entityScores, Bundle.EMPTY, urlSpan);
+ }
+
+ private Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+ @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+ mLinks.add(new TextLink(
+ start, end, new EntityConfidence(entityScores), extras, urlSpan));
return this;
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index f23691586a2a..4a6f3e53b223 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -24,10 +24,12 @@ import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Locale;
@@ -209,6 +211,7 @@ public final class TextSelection implements Parcelable {
@Nullable private final LocaleList mDefaultLocales;
private final boolean mDarkLaunchAllowed;
private final Bundle mExtras;
+ @Nullable private String mCallingPackageName;
private Request(
CharSequence text,
@@ -270,6 +273,26 @@ public final class TextSelection implements Parcelable {
}
/**
+ * Sets the name of the package that is sending this request.
+ * <p>
+ * Package-private for SystemTextClassifier's use.
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setCallingPackageName(@Nullable String callingPackageName) {
+ mCallingPackageName = callingPackageName;
+ }
+
+ /**
+ * Returns the name of the package that sent this request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @Nullable
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -355,7 +378,7 @@ public final class TextSelection implements Parcelable {
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex,
+ return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
mDefaultLocales, mDarkLaunchAllowed,
mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
@@ -368,21 +391,33 @@ public final class TextSelection implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText.toString());
+ dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
- dest.writeInt(mDefaultLocales != null ? 1 : 0);
- if (mDefaultLocales != null) {
- mDefaultLocales.writeToParcel(dest, flags);
- }
+ dest.writeParcelable(mDefaultLocales, flags);
+ dest.writeString(mCallingPackageName);
dest.writeBundle(mExtras);
}
+ private static Request readFromParcel(Parcel in) {
+ final CharSequence text = in.readCharSequence();
+ final int startIndex = in.readInt();
+ final int endIndex = in.readInt();
+ final LocaleList defaultLocales = in.readParcelable(null);
+ final String callingPackageName = in.readString();
+ final Bundle extras = in.readBundle();
+
+ final Request request = new Request(text, startIndex, endIndex, defaultLocales,
+ /* darkLaunchAllowed= */ false, extras);
+ request.setCallingPackageName(callingPackageName);
+ return request;
+ }
+
public static final Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
- return new Request(in);
+ return readFromParcel(in);
}
@Override
@@ -390,15 +425,6 @@ public final class TextSelection implements Parcelable {
return new Request[size];
}
};
-
- private Request(Parcel in) {
- mText = in.readString();
- mStartIndex = in.readInt();
- mEndIndex = in.readInt();
- mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
- mDarkLaunchAllowed = false;
- mExtras = in.readBundle();
- }
}
@Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a9735959d588..90da81276ba3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10217,7 +10217,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
// ContentCapture
- if (isImportantForContentCapture() && isTextEditable()) {
+ if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
if (cm != null && cm.isContentCaptureEnabled()) {
// TODO(b/111276913): pass flags when edited by user / add CTS test
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 8bc90a891352..a27dbeae2f3a 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -39,6 +39,7 @@ import android.provider.DocumentsContract.Document;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.MetadataReader;
+import android.system.Int64Ref;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -53,6 +54,12 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -122,18 +129,56 @@ public abstract class FileSystemProvider extends DocumentsProvider {
throw new FileNotFoundException("Can't find the file for documentId: " + documentId);
}
+ final String mimeType = getDocumentType(documentId);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ final Int64Ref treeCount = new Int64Ref(0);
+ final Int64Ref treeSize = new Int64Ref(0);
+ try {
+ final Path path = FileSystems.getDefault().getPath(file.getAbsolutePath());
+ Files.walkFileTree(path, new FileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ treeCount.value += 1;
+ treeSize.value += attrs.size();
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "An error occurred retrieving the metadata", e);
+ return null;
+ }
+
+ final Bundle res = new Bundle();
+ res.putLong(DocumentsContract.METADATA_TREE_COUNT, treeCount.value);
+ res.putLong(DocumentsContract.METADATA_TREE_SIZE, treeSize.value);
+ return res;
+ }
+
if (!file.isFile()) {
Log.w(TAG, "Can't stream non-regular file. Returning empty metadata.");
return null;
}
-
if (!file.canRead()) {
Log.w(TAG, "Can't stream non-readable file. Returning empty metadata.");
return null;
}
-
- String mimeType = getDocumentType(documentId);
if (!MetadataReader.isSupportedMimeType(mimeType)) {
+ Log.w(TAG, "Unsupported type " + mimeType + ". Returning empty metadata.");
return null;
}
@@ -562,7 +607,8 @@ public abstract class FileSystemProvider extends DocumentsProvider {
}
protected boolean typeSupportsMetadata(String mimeType) {
- return MetadataReader.isSupportedMimeType(mimeType);
+ return MetadataReader.isSupportedMimeType(mimeType)
+ || Document.MIME_TYPE_DIR.equals(mimeType);
}
protected final File getFileForDocId(String docId) throws FileNotFoundException {
diff --git a/core/java/com/android/internal/os/AppIdToPackageMap.java b/core/java/com/android/internal/os/AppIdToPackageMap.java
new file mode 100644
index 000000000000..65aa989bbb38
--- /dev/null
+++ b/core/java/com/android/internal/os/AppIdToPackageMap.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import android.app.AppGlobals;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Maps AppIds to their package names. */
+public final class AppIdToPackageMap {
+ private final Map<Integer, String> mAppIdToPackageMap;
+
+ @VisibleForTesting
+ public AppIdToPackageMap(Map<Integer, String> appIdToPackageMap) {
+ mAppIdToPackageMap = appIdToPackageMap;
+ }
+
+ /** Creates a new {@link AppIdToPackageMap} for currently installed packages. */
+ public static AppIdToPackageMap getSnapshot() {
+ List<PackageInfo> packages;
+ try {
+ packages = AppGlobals.getPackageManager()
+ .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE,
+ UserHandle.USER_SYSTEM).getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ final Map<Integer, String> map = new HashMap<>();
+ for (PackageInfo pkg : packages) {
+ final int uid = pkg.applicationInfo.uid;
+ if (pkg.sharedUserId != null && map.containsKey(uid)) {
+ // Use sharedUserId string as package name if there are collisions
+ map.put(uid, "shared:" + pkg.sharedUserId);
+ } else {
+ map.put(uid, pkg.packageName);
+ }
+ }
+ return new AppIdToPackageMap(map);
+ }
+
+ /** Maps the AppId to a package name. */
+ public String mapAppId(int appId) {
+ String pkgName = mAppIdToPackageMap.get(appId);
+ return pkgName == null ? String.valueOf(appId) : pkgName;
+ }
+
+ /** Maps the UID to a package name. */
+ public String mapUid(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ final String pkgName = mAppIdToPackageMap.get(appId);
+ final String uidStr = UserHandle.formatUid(uid);
+ return pkgName == null ? uidStr : pkgName + '/' + uidStr;
+ }
+}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index ff34036ce7e9..5465485790be 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -21,8 +21,6 @@ import android.annotation.Nullable;
import android.os.Binder;
import android.os.Process;
import android.os.SystemClock;
-import android.os.ThreadLocalWorkSource;
-import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.Pair;
@@ -62,7 +60,11 @@ public class BinderCallsStats implements BinderInternal.Observer {
private static final int CALL_SESSIONS_POOL_SIZE = 100;
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
+ // Default values for overflow entry. The work source uid does not use a default value in order
+ // to have on overflow entry per work source uid.
private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
+ private static final boolean OVERFLOW_SCREEN_INTERACTIVE = false;
+ private static final int OVERFLOW_DIRECT_CALLING_UID = -1;
private static final int OVERFLOW_TRANSACTION_CODE = -1;
// Whether to collect all the data: cpu + exceptions + reply/request sizes.
@@ -106,7 +108,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
@Override
@Nullable
- public CallSession callStarted(Binder binder, int code) {
+ public CallSession callStarted(Binder binder, int code, int workSourceUid) {
if (mDeviceState == null || mDeviceState.isCharging()) {
return null;
}
@@ -130,19 +132,21 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
@Override
- public void callEnded(@Nullable CallSession s, int parcelRequestSize, int parcelReplySize) {
+ public void callEnded(@Nullable CallSession s, int parcelRequestSize,
+ int parcelReplySize, int workSourceUid) {
if (s == null) {
return;
}
- processCallEnded(s, parcelRequestSize, parcelReplySize);
+ processCallEnded(s, parcelRequestSize, parcelReplySize, workSourceUid);
if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
mCallSessionsPool.add(s);
}
}
- private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+ private void processCallEnded(CallSession s,
+ int parcelRequestSize, int parcelReplySize, int workSourceUid) {
// Non-negative time signals we need to record data for this call.
final boolean recordCall = s.cpuTimeStarted >= 0;
final long duration;
@@ -155,7 +159,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
latencyDuration = 0;
}
final int callingUid = getCallingUid();
- final int workSourceUid = getWorkSourceUid();
synchronized (mLock) {
// This was already checked in #callStart but check again while synchronized.
@@ -356,14 +359,13 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
/** Writes the collected statistics to the supplied {@link PrintWriter}.*/
- public void dump(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap, boolean verbose) {
+ public void dump(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
synchronized (mLock) {
- dumpLocked(pw, appIdToPkgNameMap, verbose);
+ dumpLocked(pw, packageMap, verbose);
}
}
- private void dumpLocked(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap,
- boolean verbose) {
+ private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
long totalCallsCount = 0;
long totalRecordedCallsCount = 0;
long totalCpuTime = 0;
@@ -397,9 +399,9 @@ public class BinderCallsStats implements BinderInternal.Observer {
for (ExportedCallStat e : exportedCallStats) {
sb.setLength(0);
sb.append(" ")
- .append(uidToString(e.callingUid, appIdToPkgNameMap))
+ .append(packageMap.mapUid(e.callingUid))
.append(',')
- .append(uidToString(e.workSourceUid, appIdToPkgNameMap))
+ .append(packageMap.mapUid(e.workSourceUid))
.append(',').append(e.className)
.append('#').append(e.methodName)
.append(',').append(e.screenInteractive)
@@ -420,7 +422,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
final List<UidEntry> summaryEntries = verbose ? entries
: getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
for (UidEntry entry : summaryEntries) {
- String uidStr = uidToString(entry.workSourceUid, appIdToPkgNameMap);
+ String uidStr = packageMap.mapUid(entry.workSourceUid);
pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
entry.recordedCallCount, entry.callCount, uidStr));
@@ -448,13 +450,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
}
- private static String uidToString(int uid, Map<Integer, String> pkgNameMap) {
- final int appId = UserHandle.getAppId(uid);
- final String pkgName = pkgNameMap == null ? null : pkgNameMap.get(appId);
- final String uidStr = UserHandle.formatUid(uid);
- return pkgName == null ? uidStr : pkgName + '/' + uidStr;
- }
-
protected long getThreadTimeMicro() {
return SystemClock.currentThreadTimeMicro();
}
@@ -463,10 +458,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
return Binder.getCallingUid();
}
- protected int getWorkSourceUid() {
- return ThreadLocalWorkSource.getUid();
- }
-
protected long getElapsedRealtimeMicro() {
return SystemClock.elapsedRealtimeNanos() / 1000;
}
@@ -669,14 +660,16 @@ public class BinderCallsStats implements BinderInternal.Observer {
// Only create CallStat if it's a new entry, otherwise update existing instance.
if (mapCallStat == null) {
if (maxCallStatsReached) {
- mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE,
- screenInteractive);
+ mapCallStat = get(OVERFLOW_DIRECT_CALLING_UID, OVERFLOW_BINDER,
+ OVERFLOW_TRANSACTION_CODE, OVERFLOW_SCREEN_INTERACTIVE);
if (mapCallStat != null) {
return mapCallStat;
}
+ callingUid = OVERFLOW_DIRECT_CALLING_UID;
binderClass = OVERFLOW_BINDER;
transactionCode = OVERFLOW_TRANSACTION_CODE;
+ screenInteractive = OVERFLOW_SCREEN_INTERACTIVE;
}
mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 015506722c73..5b699791ea77 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -22,7 +22,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.EventLog;
-import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
@@ -86,6 +85,22 @@ public class BinderInternal {
boolean exceptionThrown;
}
+
+ /**
+ * Responsible for resolving a work source.
+ */
+ @FunctionalInterface
+ public interface WorkSourceProvider {
+ /**
+ * <p>This method is called in a critical path of the binder transaction.
+ * <p>The implementation should never execute a binder call since it is called during a
+ * binder transaction.
+ *
+ * @return the uid of the process to attribute the binder transaction to.
+ */
+ int resolveWorkSourceUid();
+ }
+
/**
* Allows to track various steps of an API call.
*/
@@ -95,14 +110,16 @@ public class BinderInternal {
*
* @return a CallSession to pass to the callEnded method.
*/
- CallSession callStarted(Binder binder, int code);
+ CallSession callStarted(Binder binder, int code, int workSourceUid);
/**
* Called when a binder call stops.
*
- * <li>This method will be called even when an exception is thrown.
+ * <li>This method will be called even when an exception is thrown by the binder stub
+ * implementation.
*/
- void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize);
+ void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize,
+ int workSourceUid);
/**
* Called if an exception is thrown while executing the binder transaction.
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b97a9fa8d1cc..b00e6fdd5e9a 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,7 +25,6 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.text.TextUtils;
@@ -78,10 +77,23 @@ public class SystemConfig {
final ArrayList<SplitPermissionInfo> mSplitPermissions = new ArrayList<>();
+ public static final class SharedLibraryEntry {
+ public final String name;
+ public final String filename;
+ public final String[] dependencies;
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies) {
+ this.name = name;
+ this.filename = filename;
+ this.dependencies = dependencies;
+ }
+ }
+
// These are the built-in shared libraries that were read from the
- // system configuration files. Keys are the library names; strings are the
- // paths to the libraries.
- final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>();
+ // system configuration files. Keys are the library names; values are
+ // the individual entries that contain information such as filename
+ // and dependencies.
+ final ArrayMap<String, SharedLibraryEntry> mSharedLibraries = new ArrayMap<>();
// These are the features this devices supports that were read from the
// system configuration files.
@@ -200,7 +212,7 @@ public class SystemConfig {
return mSplitPermissions;
}
- public ArrayMap<String, String> getSharedLibraries() {
+ public ArrayMap<String, SharedLibraryEntry> getSharedLibraries() {
return mSharedLibraries;
}
@@ -497,6 +509,7 @@ public class SystemConfig {
} else if ("library".equals(name) && allowLibs) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
+ String ldependency = parser.getAttributeValue(null, "dependency");
if (lname == null) {
Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
@@ -505,11 +518,12 @@ public class SystemConfig {
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
- mSharedLibraries.put(lname, lfile);
+ SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
+ ldependency == null ? new String[0] : ldependency.split(":"));
+ mSharedLibraries.put(lname, entry);
}
XmlUtils.skipCurrentTag(parser);
continue;
-
} else if ("feature".equals(name) && allowFeatures) {
String fname = parser.getAttributeValue(null, "name");
int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 31bb1d57d702..8962e1d8c727 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -132,7 +132,6 @@ cc_library_shared {
"android/graphics/GIFMovie.cpp",
"android/graphics/GraphicBuffer.cpp",
"android/graphics/Graphics.cpp",
- "android/graphics/HarfBuzzNGFaceSkia.cpp",
"android/graphics/ImageDecoder.cpp",
"android/graphics/Interpolator.cpp",
"android/graphics/MaskFilter.cpp",
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
deleted file mode 100644
index cfe742d0134d..000000000000
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define LOG_TAG "TextLayoutCache"
-
-#include "HarfBuzzNGFaceSkia.h"
-
-#include <stdlib.h>
-
-#include <log/log.h>
-
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkPoint.h>
-#include <SkRect.h>
-#include <SkTypeface.h>
-
-#include <hb.h>
-
-namespace android {
-
-static const bool kDebugGlyphs = false;
-
-// Our implementation of the callbacks which Harfbuzz requires by using Skia
-// calls. See the Harfbuzz source for references about what these callbacks do.
-
-struct HarfBuzzFontData {
- explicit HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { }
- SkPaint* m_paint;
-};
-
-static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
-{
- ALOG_ASSERT(codepoint <= 0xFFFF);
- paint->setTextEncoding(kGlyphID_SkTextEncoding);
-
- SkScalar skWidth;
- SkRect skBounds;
- uint16_t glyph = codepoint;
-
- paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
- if (kDebugGlyphs) {
- ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
- }
- if (width)
- *width = SkScalarToHBFixed(skWidth);
- if (extents) {
- // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up.
- extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft);
- extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop);
- extents->width = SkScalarToHBFixed(skBounds.width());
- extents->height = SkScalarToHBFixed(-skBounds.height());
- }
-}
-
-static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
- SkPaint* paint = hbFontData->m_paint;
- paint->setTextEncoding(kUTF32_SkTextEncoding);
-
- if (unicode > 0x10ffff) {
- unicode = 0xfffd;
- }
- SkUnichar unichar = unicode;
-
- uint16_t glyph16;
- paint->textToGlyphs(&unichar, sizeof(unichar), &glyph16);
- *glyph = glyph16;
- return !!*glyph;
-}
-
-static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
- hb_position_t advance = 0;
-
- SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0);
- return advance;
-}
-
-static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
-{
- // Just return true, following the way that Harfbuzz-FreeType
- // implementation does.
- return true;
-}
-
-static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
-{
- HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
-
- SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents);
- return true;
-}
-
-static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs()
-{
- static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0;
-
- // We don't set callback functions which we can't support.
- // Harfbuzz will use the fallback implementation if they aren't set.
- if (!harfbuzzSkiaFontFuncs) {
- harfbuzzSkiaFontFuncs = hb_font_funcs_create();
- hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0);
- hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
- hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
- hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0);
- hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs);
- }
- return harfbuzzSkiaFontFuncs;
-}
-
-hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
-{
- SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
-
- const size_t tableSize = typeface->getTableSize(tag);
- if (!tableSize)
- return 0;
-
- char* buffer = reinterpret_cast<char*>(malloc(tableSize));
- if (!buffer)
- return 0;
- size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer);
- if (tableSize != actualSize) {
- free(buffer);
- return 0;
- }
-
- return hb_blob_create(const_cast<char*>(buffer), tableSize,
- HB_MEMORY_MODE_WRITABLE, buffer, free);
-}
-
-static void destroyHarfBuzzFontData(void* data) {
- delete (HarfBuzzFontData*)data;
-}
-
-hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) {
- hb_font_t* font = hb_font_create(face);
-
- // Note: this needs to be reworked when we do subpixels
- int x_ppem = floor(sizeX + 0.5);
- int y_ppem = floor(sizeY + 0.5);
- hb_font_set_ppem(font, x_ppem, y_ppem);
- hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY));
-
- HarfBuzzFontData* data = new HarfBuzzFontData(paint);
- hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData);
-
- return font;
-}
-
-} // namespace android
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 98bc735e1fae..8a280343e731 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -225,7 +225,8 @@ void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognitio
gAudioFormatCstor,
audioFormatFromNative(event->audio_config.format),
event->audio_config.sample_rate,
- inChannelMaskFromNative(event->audio_config.channel_mask));
+ inChannelMaskFromNative(event->audio_config.channel_mask),
+ (jint)0 /* channelIndexMask */);
}
if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index adab8e2bae39..283eb035c6f7 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2061,6 +2061,12 @@ android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArr
return (jint)nativeToJavaStatus(status);
}
+static jboolean
+android_media_AudioSystem_isHapticPlaybackSupported(JNIEnv *env, jobject thiz)
+{
+ return AudioSystem::isHapticPlaybackSupported();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2123,6 +2129,7 @@ static const JNINativeMethod gMethods[] = {
{"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
{"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
{"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
+ {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported},
};
static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7ebb2b289422..516093e42691 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -479,6 +479,24 @@ native_init_failure:
}
// ----------------------------------------------------------------------------
+static jboolean
+android_media_AudioTrack_is_direct_output_supported(JNIEnv *env, jobject thiz,
+ jint encoding, jint sampleRate,
+ jint channelMask, jint channelIndexMask,
+ jint contentType, jint usage, jint flags) {
+ audio_config_base_t config = {};
+ audio_attributes_t attributes = {};
+ config.format = static_cast<audio_format_t>(audioFormatToNative(encoding));
+ config.sample_rate = static_cast<uint32_t>(sampleRate);
+ config.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
+ attributes.content_type = static_cast<audio_content_type_t>(contentType);
+ attributes.usage = static_cast<audio_usage_t>(usage);
+ attributes.flags = static_cast<audio_flags_mask_t>(flags);
+ // ignore source and tags attributes as they don't affect querying whether output is supported
+ return AudioTrack::isDirectOutputSupported(config, attributes);
+}
+
+// ----------------------------------------------------------------------------
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
@@ -1297,6 +1315,9 @@ static jint android_media_AudioTrack_get_port_id(JNIEnv *env, jobject thiz) {
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
+ {"native_is_direct_output_supported",
+ "(IIIIIII)Z",
+ (void *)android_media_AudioTrack_is_direct_output_supported},
{"native_start", "()V", (void *)android_media_AudioTrack_start},
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 02867304af36..4aa88e7558cd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -276,15 +276,17 @@ static void EnableDebugger() {
}
}
- // We don't want core dumps, though, so set the soft limit on core dump size
- // to 0 without changing the hard limit.
- rlimit rl;
- if (getrlimit(RLIMIT_CORE, &rl) == -1) {
- ALOGE("getrlimit(RLIMIT_CORE) failed");
- } else {
- rl.rlim_cur = 0;
- if (setrlimit(RLIMIT_CORE, &rl) == -1) {
- ALOGE("setrlimit(RLIMIT_CORE) failed");
+ // Set the core dump size to zero unless wanted (see also coredump_setup in build/envsetup.sh).
+ if (!GetBoolProperty("persist.zygote.core_dump", false)) {
+ // Set the soft limit on core dump size to 0 without changing the hard limit.
+ rlimit rl;
+ if (getrlimit(RLIMIT_CORE, &rl) == -1) {
+ ALOGE("getrlimit(RLIMIT_CORE) failed");
+ } else {
+ rl.rlim_cur = 0;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1) {
+ ALOGE("setrlimit(RLIMIT_CORE) failed");
+ }
}
}
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index fd64c651528e..514f306953a5 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -76,5 +76,11 @@ enum PageId {
// OPEN: Settings > Developer options > Disable > Info dialog
DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
+
+ // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents)
+ SETTINGS_WIFI_DPP_CONFIGURATOR = 1595;
+
+ // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
+ SETTINGS_WIFI_DPP_ENROLLEE = 1596;
}
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index ed040f43253a..f7dcee282a57 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -206,6 +206,8 @@ message UsbPortInfoProto {
optional bool can_change_mode = 3;
optional bool can_change_power_role = 4;
optional bool can_change_data_role = 5;
+ optional int64 connected_at_millis = 6;
+ optional int64 last_connect_duration_millis = 7;
}
message UsbPortProto {
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 5726d9ab0466..c7a6b68fbc00 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -51,7 +51,7 @@ enum EventId {
SET_ALWAYS_ON_VPN_PACKAGE = 26;
SET_PERMITTED_INPUT_METHODS = 27;
SET_PERMITTED_ACCESSIBILITY_SERVICES = 28;
- SET_SCREEN_CAPTURE_DISABLE = 29;
+ SET_SCREEN_CAPTURE_DISABLED = 29;
SET_CAMERA_DISABLED = 30;
QUERY_SUMMARY_FOR_USER = 31;
QUERY_SUMMARY = 32;
@@ -64,7 +64,7 @@ enum EventId {
SET_ORGANIZATION_COLOR = 39;
SET_PROFILE_NAME = 40;
SET_USER_ICON = 41;
- SET_DEVICE_OWNER_LOCKSCREEN_INFO = 42;
+ SET_DEVICE_OWNER_LOCK_SCREEN_INFO = 42;
SET_SHORT_SUPPORT_MESSAGE = 43;
SET_LONG_SUPPORT_MESSAGE = 44;
SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45;
@@ -139,4 +139,6 @@ enum EventId {
SET_GLOBAL_SETTING = 111;
PM_IS_INSTALLER_DEVICE_OWNER_OR_AFFILIATED_PROFILE_OWNER = 112;
PM_UNINSTALL = 113;
+ WIFI_SERVICE_ADD_NETWORK_SUGGESTIONS = 114;
+ WIFI_SERVICE_ADD_OR_UPDATE_NETWORK = 115;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1dd42b8162b3..9c55c752e552 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -897,7 +897,7 @@
android:protectionLevel="dangerous"
android:usageInfoRequired="true" />
- <!-- @hide @SystemApi
+ <!-- @hide @SystemApi @TestApi
Allows an application to modify OBB files visible to other apps. -->
<permission android:name="android.permission.WRITE_OBB"
android:protectionLevel="signature|privileged" />
@@ -1648,7 +1648,7 @@
@hide
-->
<permission android:name="android.permission.SUSPEND_APPS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|wellbeing" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
@@ -2087,10 +2087,9 @@
<p>This permission should <em>only</em> be requested by the platform
document management app. This permission cannot be granted to
third-party apps.
- <p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @hide Allows an application to cache content.
<p>Not for use by third-party applications.
@@ -2171,6 +2170,13 @@
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to start an activity within its managed profile from
+ the personal profile.
+ This permission is not available to third party applications.
+ @hide -->
+ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
@@ -2206,9 +2212,9 @@
android:description="@string/permdesc_reorderTasks"
android:protectionLevel="normal" />
- <!-- @hide Allows an application to change to remove/kill tasks -->
+ <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
@@ -3344,6 +3350,11 @@
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to observe role holder changes.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
@@ -4086,10 +4097,10 @@
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
- <!-- Allows the holder to access and manage instant applications on the device.
- @hide -->
+ <!-- @SystemApi Allows the holder to access and manage instant applications on the device.
+ @hide -->
<permission android:name="android.permission.ACCESS_INSTANT_APPS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier|wellbeing" />
<uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
<!-- Allows the holder to view the instant applications on the device.
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 56c2e3ed7f0a..f3b4df73dd7f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1086,7 +1086,7 @@
<string name="email" msgid="4560673117055050403">"ኢሜይል"</string>
<string name="email_desc" msgid="3638665569546416795">"ለተመረጡ አድራሻዎች ኢሜይል ላክ"</string>
<string name="dial" msgid="1253998302767701559">"ጥሪ"</string>
- <string name="dial_desc" msgid="6573723404985517250">"ወደተመረጠውን ስልክ ቁጥር ደውል"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"ወደ ተመረጠው ስልክ ቁጥር ደውል"</string>
<string name="map" msgid="5441053548030107189">"ካርታ"</string>
<string name="map_desc" msgid="1836995341943772348">"የተመረጠውን አድራሻ ያለበትን አግኝ"</string>
<string name="browse" msgid="1245903488306147205">"ክፈት"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2af43ce1dac9..a14a87130ff5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -537,7 +537,7 @@
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es noch einmal."</string>
- <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Finger zu schnell bewegt, bitte noch einmal versuchen"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger zu langsam bewegt. Bitte versuche es noch einmal."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
@@ -549,7 +549,7 @@
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche. Bitte versuche es später noch einmal."</string>
+ <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Zu viele Versuche. Der Fingerabdrucksensor wurde deaktiviert."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Keine Fingerabdrücke erfasst."</string>
@@ -581,7 +581,7 @@
<string name="face_error_no_space" msgid="8224993703466381314">"Gesicht kann nicht gespeichert werden."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
<string name="face_error_user_canceled" msgid="8943921120862164539">"Gesichtsauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche. Versuch es später noch einmal."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
<string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zu viele Versuche. Gesichtserkennung deaktiviert."</string>
<string name="face_error_unable_to_process" msgid="238761109287767270">"Versuch es noch einmal."</string>
<string name="face_error_not_enrolled" msgid="9166792142679691323">"Kein Gesicht erfasst."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 77a8f054ad54..e633ab46bf25 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1084,27 +1084,27 @@
<string name="inputMethod" msgid="1653630062304567879">"Sisestusmeetod"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoimingud"</string>
<string name="email" msgid="4560673117055050403">"E-post"</string>
- <string name="email_desc" msgid="3638665569546416795">"Valitud aadressile meili saatmine"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Saada valitud aadressile meil"</string>
<string name="dial" msgid="1253998302767701559">"Helista"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Valitud telefoninumbrile helistamine"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Helista valitud telefoninumbrile"</string>
<string name="map" msgid="5441053548030107189">"Kaart"</string>
- <string name="map_desc" msgid="1836995341943772348">"Valitud aadressi leidmine"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Leia valitud aadress"</string>
<string name="browse" msgid="1245903488306147205">"Ava"</string>
- <string name="browse_desc" msgid="8220976549618935044">"Valitud URL-i avamine"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"Ava valitud URL"</string>
<string name="sms" msgid="4560537514610063430">"Saada sõnum"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Valitud telefoninumbrile sõnumi saatmine"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Saada valitud telefoninumbrile sõnum"</string>
<string name="add_contact" msgid="7867066569670597203">"Lisa"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Kontaktide hulka lisamine"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Lisa kontaktide hulka"</string>
<string name="view_calendar" msgid="979609872939597838">"Kuva"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Valitud aja vaatamine kalendris"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Kuva valitud aeg kalendris"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Lisa ajakavasse"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Ürituse ajastamine valitud ajale"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Ajasta üritus valitud ajale"</string>
<string name="view_flight" msgid="7691640491425680214">"Jälgi"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Valitud lennu jälgimine"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Jälgi valitud lendu"</string>
<string name="translate" msgid="9218619809342576858">"Tõlgi"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Tõlkige valitud tekst"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Tõlgi valitud tekst"</string>
<string name="define" msgid="7394820043869954211">"Defineeri"</string>
- <string name="define_desc" msgid="7910883642444919726">"Valitud teksti defineerimine"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Defineeri valitud tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index bc367be63a54..3f8f0d126032 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1097,7 +1097,7 @@
<string name="add_contact_desc" msgid="4830217847004590345">"Lisää yhteystietoihin"</string>
<string name="view_calendar" msgid="979609872939597838">"Näytä"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Näytä valittu aika kalenterissa"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Aikataulu"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Aikatauluta"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ajoita tapahtuma valitulle ajalle"</string>
<string name="view_flight" msgid="7691640491425680214">"Seuraa"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seuraa valittua lentoa"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index fccb289452a2..87e1f9785591 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -306,7 +306,7 @@
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"कॉल लॉग की जानकारी देखना और उसमें बदलाव करना"</string>
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने काॅल लाॅग एक्सेस करने की मंज़ूरी देना चाहते हैं?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"फ़ोन"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करें और प्रबंधित करें"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"शरीर संवेदक"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 69b4db23dfbe..cd2af7bb65bf 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1097,7 +1097,7 @@
<string name="add_contact_desc" msgid="4830217847004590345">"បញ្ចូល​ទៅ​ក្នុង​ទំនាក់ទំនង"</string>
<string name="view_calendar" msgid="979609872939597838">"មើល"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"មើល​ពេលវេលា​ដែល​បាន​ជ្រើសរើស​នៅក្នុង​ប្រតិទិន"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"កាលវិភាគ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"កំណត់​កាលវិភាគ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"កំណត់​កាលវិភាគ​ព្រឹត្តិការណ៍​សម្រាប់ពេល​វេលាដែល​បាន​ជ្រើសរើស"</string>
<string name="view_flight" msgid="7691640491425680214">"តាម​ដាន"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"តាមដាន​ជើង​ហោះហើរ​ដែល​បាន​ជ្រើសរើស"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 2232f4d07d4d..f6e9b7215960 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1083,15 +1083,15 @@
<string name="deleteText" msgid="6979668428458199034">"Жок кылуу"</string>
<string name="inputMethod" msgid="1653630062304567879">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Текст боюнча иштер"</string>
- <string name="email" msgid="4560673117055050403">"Электрондук почта"</string>
+ <string name="email" msgid="4560673117055050403">"Кат жөнөтүү"</string>
<string name="email_desc" msgid="3638665569546416795">"Тандалган дарекке электрондук кат жөнөтүү"</string>
<string name="dial" msgid="1253998302767701559">"Чалуу"</string>
<string name="dial_desc" msgid="6573723404985517250">"Тандалган телефон номерине чалуу"</string>
- <string name="map" msgid="5441053548030107189">"Карта"</string>
+ <string name="map" msgid="5441053548030107189">"Картадан кароо"</string>
<string name="map_desc" msgid="1836995341943772348">"Тандалган даректи картада табуу"</string>
<string name="browse" msgid="1245903488306147205">"Ачуу"</string>
<string name="browse_desc" msgid="8220976549618935044">"Тандалган URL\'ди ачуу"</string>
- <string name="sms" msgid="4560537514610063430">"Билдирүү"</string>
+ <string name="sms" msgid="4560537514610063430">"Билдирүү жазуу"</string>
<string name="sms_desc" msgid="7526588350969638809">"Тандалган телефон номерине билдирүү жөнөтүү"</string>
<string name="add_contact" msgid="7867066569670597203">"Кошуу"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Байланыштарга кошуу"</string>
@@ -1103,7 +1103,7 @@
<string name="view_flight_desc" msgid="3876322502674253506">"Тандалган аба каттамына көз салуу"</string>
<string name="translate" msgid="9218619809342576858">"Которуу"</string>
<string name="translate_desc" msgid="4502367770068777202">"Тандалган текстти которуу"</string>
- <string name="define" msgid="7394820043869954211">"Төмөнкү сөздүн аныктамасын бериңиз:"</string>
+ <string name="define" msgid="7394820043869954211">"Аныктоо"</string>
<string name="define_desc" msgid="7910883642444919726">"Тандалган текстти аныктоо"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сактагычта орун калбай баратат"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index eec4b31f6eb1..dd4b125681c4 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1107,7 +1107,7 @@
<string name="email_desc" msgid="3638665569546416795">"Nosūtīt e-pasta ziņojumu uz atlasīto adresi"</string>
<string name="dial" msgid="1253998302767701559">"Zvanīt"</string>
<string name="dial_desc" msgid="6573723404985517250">"Zvanīt uz atlasīto tālruņa numuru"</string>
- <string name="map" msgid="5441053548030107189">"Maps"</string>
+ <string name="map" msgid="5441053548030107189">"Karte"</string>
<string name="map_desc" msgid="1836995341943772348">"Atrast atlasīto adresi"</string>
<string name="browse" msgid="1245903488306147205">"Atvērt"</string>
<string name="browse_desc" msgid="8220976549618935044">"Atvērt atlasīto URL"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index ac2827053b01..93f7e37480cf 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1083,23 +1083,23 @@
<string name="deleteText" msgid="6979668428458199034">"Устгах"</string>
<string name="inputMethod" msgid="1653630062304567879">"Оруулах арга"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Текст үйлдэл"</string>
- <string name="email" msgid="4560673117055050403">"Имэйл"</string>
+ <string name="email" msgid="4560673117055050403">"Имэйл бичих"</string>
<string name="email_desc" msgid="3638665569546416795">"Сонгосон хаяг руу имэйл илгээх"</string>
<string name="dial" msgid="1253998302767701559">"Залгах"</string>
<string name="dial_desc" msgid="6573723404985517250">"Сонгосон утасны дугаар руу залгах"</string>
- <string name="map" msgid="5441053548030107189">"Газрын зураг"</string>
+ <string name="map" msgid="5441053548030107189">"Газрын зураг хийх"</string>
<string name="map_desc" msgid="1836995341943772348">"Сонгосон хаягийг байршуулах"</string>
<string name="browse" msgid="1245903488306147205">"Нээх"</string>
<string name="browse_desc" msgid="8220976549618935044">"Сонгосон URL-г нээх"</string>
- <string name="sms" msgid="4560537514610063430">"Зурвас"</string>
+ <string name="sms" msgid="4560537514610063430">"Зурвас бичих"</string>
<string name="sms_desc" msgid="7526588350969638809">"Сонгосон утасны дугаар руу мессеж илгээх"</string>
<string name="add_contact" msgid="7867066569670597203">"Нэмэх"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Харилцагчид нэмэх"</string>
<string name="view_calendar" msgid="979609872939597838">"Үзэх"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Календариас сонгосон огноог харах"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Хуваарь"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Хуваарь гаргах"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Aрга хэмжээг сонгосон цагт хуваарилах"</string>
- <string name="view_flight" msgid="7691640491425680214">"Бичлэг"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Хянах"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Сонгосон нислэгийг хянах"</string>
<string name="translate" msgid="9218619809342576858">"Орчуулах"</string>
<string name="translate_desc" msgid="4502367770068777202">"Сонгосон текстийг орчуулах"</string>
diff --git a/core/res/res/values-mr-watch/strings.xml b/core/res/res/values-mr-watch/strings.xml
index 223f8faf1b88..f8a52148ed4e 100644
--- a/core/res/res/values-mr-watch/strings.xml
+++ b/core/res/res/values-mr-watch/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
+ <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अ‍ॅप"</string>
<string name="permgrouplab_sensors" msgid="202675452368612754">"सेन्सर"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9a58b386ed6f..902380b6696f 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -183,7 +183,7 @@
<string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"आपल्या कार्य प्रोफाइल प्रशासकाद्वारे"</string>
<string name="ssl_ca_cert_noti_managed" msgid="4030263497686867141">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g> द्वारे"</string>
<string name="work_profile_deleted" msgid="5005572078641980632">"कार्य प्रोफाईल हटविले"</string>
- <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, तुमचे कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
+ <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अ‍ॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, तुमचे कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"तुमचे कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्‍ध नाही"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"बर्‍याचदा पासवर्ड टाकण्‍याचा प्रयत्‍न केला"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"डिव्हाइस व्यवस्थापित केले आहे"</string>
@@ -281,9 +281,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
- <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अ‍ॅप वापरत असताना, अ‍ॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
<string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"या डिव्हाइसचे स्थान अॅक्सेस करण्याची &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला नेहेमी अनुमती द्यायची का?"</string>
- <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अ‍ॅप वापरत नसलात तरीही, अ‍ॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"कॅलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"आपल्या कॅलेंडरवर प्रवेश"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमचे कॅलेंडर अॅक्सेस करू द्यायचे?"</string>
@@ -330,11 +330,11 @@
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"फिंगरप्रिंट जेश्चर"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्‍हाइसच्‍या फिंगरप्रिंट सेंसरवर केलेले जेश्चर कॅप्‍चर करू शकते."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
- <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
- <string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_statusBarService" msgid="716113660795976060">"स्टेटस बार होण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्‍टेटस बार विस्तृत करा/संकुचित करा"</string>
- <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_install_shortcut" msgid="4279070216371564234">"शॉर्टकट स्‍थापित करा"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"अनुप्रयोगाला वापरकर्ता हस्‍तक्षेपाशिवाय मुख्‍यस्‍क्रीन शॉर्टकट जोडण्‍याची अनुमती देते."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"शॉर्टकट विस्‍थापित करा"</string>
@@ -344,103 +344,103 @@
<string name="permlab_answerPhoneCalls" msgid="4077162841226223337">"फोन कॉलचे उत्तर द्या"</string>
<string name="permdesc_answerPhoneCalls" msgid="2901889867993572266">"येणार्‍या फोन कॉलचे उत्तर देण्यास अॅपला अनुमती देते."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"मजकूर मेसेज मिळवा (SMS)"</string>
- <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अ‍ॅप ला अनुमती देते. म्हणजेच अ‍ॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"मजकूर मेसेज मिळवा (MMS)"</string>
- <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अ‍ॅप ला अनुमती देते. म्हणजेच अ‍ॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण मेसेज वाचा"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अ‍ॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अ‍ॅप्स व्यत्यय आणू शकतात."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता घेतलेली फीड वाचा"</string>
- <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"SMS मेसेज पाठवणे आणि पाहणे"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS मेसेज पाठविण्यासाठी अॅप ला अनुमती देते. हे अनपेक्षित शुल्कामुळे होऊ शकते. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय मेसेज पाठवल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS मेसेज पाठविण्यासाठी अ‍ॅप ला अनुमती देते. हे अनपेक्षित शुल्कामुळे होऊ शकते. दुर्भावनापूर्ण अ‍ॅप्स नी आपल्या पुष्टिकरणाशिवाय मेसेज पाठवल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"तुमचे मजकूर मेसेज वाचा (SMS किंवा MMS)"</string>
- <string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"हा अॅप तुमच्या टॅब्लेटवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
- <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
- <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"हा अ‍ॅप तुमच्या टॅब्लेटवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अ‍ॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
+ <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अ‍ॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"मजकूर मेसेज मिळवा (WAP)"</string>
- <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
+ <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अ‍ॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"चालणारे अॅप्स पुनर्प्राप्त करा"</string>
- <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्‍या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
+ <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्‍या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अ‍ॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अ‍ॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अ‍ॅप ला अनुमती देऊ शकतात."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"प्रोफाईल मालक आणि डिव्हाइस मालक सेट करण्याची अॅप्सना अनुमती द्या."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"चालणारे अॅप्स पुनर्क्रमित करा"</string>
- <string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अॅप ला अनुमती देते. अॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
+ <string name="permdesc_reorderTasks" msgid="7734217754877439351">"समोर आणि पार्श्वभूमीवर कार्ये हलविण्यासाठी अ‍ॅप ला अनुमती देते. अ‍ॅप हे आपल्या इनपुटशिवाय करू शकतो."</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"कार मोड सुरू करा"</string>
- <string name="permdesc_enableCarMode" msgid="4853187425751419467">"कार मोड सक्षम करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_enableCarMode" msgid="4853187425751419467">"कार मोड सक्षम करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"अन्य अॅप्स बंद करा"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"अन्य अॅप्सच्या पार्श्वभूमी प्रक्रिया समाप्त करण्यासाठी अॅप ला अनुमती देते. यामुळे अन्य अॅप्स चालणे थांबू शकते."</string>
- <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"हा अॅप इतर अॅप्सच्या शीर्षस्थानी दिसू शकतो."</string>
- <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"हे अॅप इतर अॅप्सच्या शीर्षस्थानी किंवा स्क्रीनच्या इतर भागांवर दिसू शकतो. हे सामान्य अॅप वापरात व्यत्यय आणू शकते किंवा इतर अॅप्सची डिस्प्ले पद्धत बदलू शकते."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"अन्य अ‍ॅप्सच्या पार्श्वभूमी प्रक्रिया समाप्त करण्यासाठी अ‍ॅप ला अनुमती देते. यामुळे अन्य अ‍ॅप्स चालणे थांबू शकते."</string>
+ <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"हा अ‍ॅप इतर अ‍ॅप्सच्या शीर्षस्थानी दिसू शकतो."</string>
+ <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"हे अ‍ॅप इतर अ‍ॅप्सच्या शीर्षस्थानी किंवा स्क्रीनच्या इतर भागांवर दिसू शकतो. हे सामान्य अ‍ॅप वापरात व्यत्यय आणू शकते किंवा इतर अ‍ॅप्सची डिस्प्ले पद्धत बदलू शकते."</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"पार्श्वभूमीत चालवा"</string>
- <string name="permdesc_runInBackground" msgid="7370142232209999824">"हे अॅप पार्श्वभूमीत चालू शकते. हे बॅटरी अधिक जलद संपवू शकते."</string>
+ <string name="permdesc_runInBackground" msgid="7370142232209999824">"हे अ‍ॅप पार्श्वभूमीत चालू शकते. हे बॅटरी अधिक जलद संपवू शकते."</string>
<string name="permlab_useDataInBackground" msgid="8694951340794341809">"पार्श्वभूमीत डेटा वापरा"</string>
<string name="permdesc_useDataInBackground" msgid="6049514223791806027">"हे अ‍ॅप पार्श्वभूमीत डेटा वापरू शकते. हे डेटाचा वापर वाढवू शकते."</string>
- <string name="permlab_persistentActivity" msgid="8841113627955563938">"अॅप नेहमी चालवा"</string>
- <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे टॅबलेट धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
+ <string name="permlab_persistentActivity" msgid="8841113627955563938">"अ‍ॅप नेहमी चालवा"</string>
+ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"अ‍ॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे टॅबलेट धीमा करून अन्य अ‍ॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"अॅपला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यासाठी अनुमती देते. हे टीव्ही धीमा करून इतर अॅप्सवर उपलब्ध असलेली मेमरी मर्यादित करू शकते."</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"अॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे फोन धीमा करून अन्य अॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"अ‍ॅप ला मेमरीमध्ये कायम असलेले त्याचे स्वतःचे भाग बनविण्यास अनुमती देते. हे फोन धीमा करून अन्य अ‍ॅप्सवर उपलब्ध असलेल्या मेमरीवर मर्यादा घालू शकते."</string>
<string name="permlab_foregroundService" msgid="3310786367649133115">"पृष्‍ठभाग सेवा रन करा"</string>
<string name="permdesc_foregroundService" msgid="6471634326171344622">"अ‍ॅपला पृष्‍ठभाग सेवा वापरण्याची अनुमती देते."</string>
- <string name="permlab_getPackageSize" msgid="7472921768357981986">"अॅप संचयन स्थान मोजा"</string>
- <string name="permdesc_getPackageSize" msgid="3921068154420738296">"अॅप ला त्याचा कोड, डेटा आणि कॅश आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
+ <string name="permlab_getPackageSize" msgid="7472921768357981986">"अ‍ॅप संचयन स्थान मोजा"</string>
+ <string name="permdesc_getPackageSize" msgid="3921068154420738296">"अ‍ॅप ला त्याचा कोड, डेटा आणि कॅश आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"सिस्टम सेटिंग्ज सुधारित करा"</string>
- <string name="permdesc_writeSettings" msgid="7775723441558907181">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
+ <string name="permdesc_writeSettings" msgid="7775723441558907181">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"सुरूवातीस चालवा"</string>
- <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर टॅबलेटला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"सिस्टम बूट करणे समाप्त करते तसेच अॅपने स्वतः प्रारंभ करण्यास त्याला अनुमती देते. यामुळे टीव्ही प्रारंभ करण्यासाठी त्यास जास्त वेळ लागू शकतो आणि नेहमी चालू ठेवून संपूर्ण टॅबलेट धीमे करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर फोनला धीमे करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे फोन प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी चालू राहून एकंदर फोनला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_broadcastSticky" msgid="7919126372606881614">"रोचक प्रसारण पाठवा"</string>
- <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"रोचक प्रसारणे पाठविण्यासाठी अ‍ॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"रोचक प्रसारणे पाठविण्यास अॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर तसेच रहाते. अतिरिक्त वापर टीव्ही धीमा किंवा यासाठी बरीच मेमरी वापरली जात असल्यामुळे तो अस्थिर करू शकतो."</string>
- <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
+ <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अ‍ॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"तुमचे संपर्क वाचा"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अ‍ॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अ‍ॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अ‍ॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"तुम्ही विशिष्ट लोकांना इतर मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप्सला अनुमती देतात. ही परवागनी अॅप्सला तुमचा संपर्क डेटा सेव्ह करण्यासाठी अनुमती देते आणि दुर्भावनापूर्ण अॅप्स तुम्हाला न कळविता संपर्क डेटा शेअर करू शकतात."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अ‍ॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अ‍ॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अ‍ॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"तुमचे संपर्क सुधारित करा"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅपला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"कॉल लॉग वाचा"</string>
- <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string>
+ <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अ‍ॅप तुमचा कॉल इतिहास वाचू शकता."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिहा"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टीव्हीचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्‍या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
- <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अ‍ॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अ‍ॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अ‍ॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
<string name="permlab_writeCalendar" msgid="8438874755193825647">"कॅलेंडर इव्हेंट जोडा किंवा बदला आणि मालकाला न कळवता अतिथींना ईमेल पाठवा"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अ‍ॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अ‍ॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अ‍ॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अ‍ॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अ‍ॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अ‍ॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेश अॅक्सेस करा"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अ‍ॅपला अतिरिक्त स्‍थान प्रदाता आदेशावर प्रवेश करण्‍याची अनुमती देते. हे कदाचित अ‍ॅपला GPS किंवा इतर स्‍थान स्त्रोत च्या ऑपरेशनमध्‍ये हस्तक्षेप करण्‍याची अनुमती देऊ शकते."</string>
<string name="permlab_accessFineLocation" msgid="6265109654698562427">"फक्त फोरग्राउंडमध्ये अचूकपणे अ‍ॅक्सेस करा"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच तुमचे अचूक स्थान मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अ‍ॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
<string name="permlab_accessCoarseLocation" msgid="3707180371693213469">"फक्त फोरग्राउंडमध्ये अंदाजे स्थान (नेटवर्क आधारित) अ‍ॅक्सेस करा"</string>
- <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टॅबलेटवर उपलब्ध करणे आवश्यक आहे."</string>
- <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टीव्हीवर उपलब्ध करणे आवश्यक आहे."</string>
- <string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. ते वापरण्याकरता अॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="tablet" msgid="8594719010575779120">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अ‍ॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टॅबलेटवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="tv" msgid="3027871910200890806">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. त्या वापरण्याकरता अ‍ॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या टीव्हीवर उपलब्ध करणे आवश्यक आहे."</string>
+ <string name="permdesc_accessCoarseLocation" product="default" msgid="854896049371048754">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच, सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतवर आधारित तुमचे स्थान मिळवू शकते. ते वापरण्याकरता अ‍ॅपसाठी, या स्थान सेवा सुरू करणे आणि त्या तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे."</string>
<string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"बॅकग्राउंडमध्ये स्थान अॅक्सेस करू शकतो"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अॅप स्थान अॅक्सेस करू शकतो."</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अ‍ॅप स्थान अॅक्सेस करू शकतो."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
- <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडिओ रेकॉर्ड"</string>
- <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
+ <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अ‍ॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"सिम वर कमांड पाठवा"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"अ‍ॅप ला सिम वर कमांड पाठविण्‍याची अनुमती देते. हे खूप धोकादायक असते."</string>
<string name="permlab_activityRecognition" msgid="3634590230567608356">"शारीरिक अॅक्टिव्हिटी ओळखा"</string>
- <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
+ <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अ‍ॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
<string name="permlab_camera" msgid="3616391919559751192">"चित्रे आणि व्हिडिओ घ्या"</string>
- <string name="permdesc_camera" msgid="5392231870049240670">"हा अॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्ह‍िडिअो रेकॉर्ड करू शकतो."</string>
+ <string name="permdesc_camera" msgid="5392231870049240670">"हा अ‍ॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्ह‍िडिअो रेकॉर्ड करू शकतो."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"व्हायब्रेट नियंत्रित करा"</string>
- <string name="permdesc_vibrate" msgid="6284989245902300945">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
+ <string name="permdesc_vibrate" msgid="6284989245902300945">"अ‍ॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"फोन नंबरवर प्रत्यक्ष कॉल करा"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अ‍ॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अ‍ॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अ‍ॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे तुमचे पैसे खर्च होऊ शकतात."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS कॉल सेवा अॅक्सेस करा"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"आपल्‍या हस्तक्षेपाशिवाय अ‍ॅपला कॉल करण्‍यासाठी IMS सेवा वापरण्याची अनुमती देते."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"फोन स्थिती आणि ओळख वाचा"</string>
@@ -454,59 +454,59 @@
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"टॅबलेट निष्क्रिय होण्यापासून प्रतिबंधित करा"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"निष्क्रिय होण्यापासून प्रतिबंध करा"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"फोन निष्‍क्रिय होण्‍यापासून प्रतिबंधित करा"</string>
- <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"टॅब्लेटला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"टॅब्लेटला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"निष्क्रिय होण्यापासून टीव्हीला प्रतिबंध करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"फोनला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"फोनला निष्क्रिय होण्यापासून प्रतिबंधित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_transmitIr" msgid="7545858504238530105">"इन्फ्रारेड प्रक्षेपण करा"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"अ‍ॅप ला टॅब्‍लेटच्‍या इन्‍फ्रारेड ट्रान्‍समीटरचा वापर करण्‍याची अनुमती देते."</string>
<string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"टीव्हीचे इन्फ्रारेड ट्रान्समीटर वापरण्यासाठी अॅपला अनुमती देते."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"अ‍ॅप ला फोनच्‍या इन्‍फ्रारेड ट्रान्‍समीटरचा वापर करण्‍याची अनुमती देते."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"वॉलपेपर सेट करा"</string>
- <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"तुमचा वॉलपेपर आकार समायोजित करा"</string>
- <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"टाइम झोन सेट करा"</string>
- <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"टीव्हीचा टाईम झोन बदलण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"फोनचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"फोनचा टाइम झोन बदलण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"डिव्हाइसवरील खाती शोधा"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"टॅब्लेटद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट होऊ शकतात."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"टॅब्लेटद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अ‍ॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट होऊ शकतात."</string>
<string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"टीव्हीद्वारे ज्ञात खात्यांची सूची मिळविण्यासाठी अॅपला अनुमती देतो. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट असू शकतात."</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"फोनद्वारे ज्ञात खात्यांची सूची मिळवण्यासाठी अ‍ॅप ला अनुमती देते. यात तुम्ही इंस्टॉल केलेल्या अनुप्रयोगांद्वारे तयार केलेली कोणतीही खाती समाविष्ट करू शकतात."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क कनेक्शन पहा"</string>
- <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"कोणती नेटवर्क अस्तित्वात आहेत आणि कनेक्ट केलेली आहेत यासारख्या नेटवर्क कनेक्शनविषयीची माहिती पाहण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"पूर्ण नेटवर्क प्रवेश आहे"</string>
- <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि कस्टम नेटवर्क प्रोटोकॉल वापरण्यासाठी अॅप ला अनुमती देते. ब्राउझर आणि अन्य अॅप्लिकेशन म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
+ <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"नेटवर्क सॉकेट तयार करण्यासाठी आणि कस्टम नेटवर्क प्रोटोकॉल वापरण्यासाठी अ‍ॅप ला अनुमती देते. ब्राउझर आणि अन्य अ‍ॅप्लिकेशन म्हणजे इंटरनेटवर डेटा पाठवण्याचा मार्ग, म्हणजे इंटरनेटवर डेटा पाठविण्यासाठी परवानगीची आवश्यकता नसते."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क कनेक्टिव्हिटी बदला"</string>
- <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"टिथर केलेली कनेक्टिव्हिटी बदला"</string>
- <string name="permdesc_changeTetherState" msgid="1524441344412319780">"टेदर केलेल्या नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeTetherState" msgid="1524441344412319780">"टेदर केलेल्या नेटवर्क कनेक्टिव्हिटीची स्थिती बदलण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"वाय-फाय कनेक्शन पहा"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"वाय-फाय सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या वाय-फाय डीव्हाइसचे नाव यासारख्या, वाय-फाय नेटवर्किंग विषयीची माहिती पाहण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"वाय-फाय सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या वाय-फाय डीव्हाइसचे नाव यासारख्या, वाय-फाय नेटवर्किंग विषयीची माहिती पाहण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"वाय-फाय वरून कनेक्ट करा आणि डिस्कनेक्ट करा"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"वाय-फाय अॅक्सेस बिंदूंवर कनेक्ट करण्यासाठी आणि त्यावरून डिस्कनेक्ट करण्यासाठी आणि वाय-फाय नेटवर्कसाठी डिव्हाइस कॉंफिगरेशनमध्ये बदल करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"वाय-फाय मल्‍टिकास्‍ट रिसेप्‍शनला अनुमती द्या"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अ‍ॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"केवळ तुमचा टीव्ही न वापरता, एकाधिक पत्ते वापरून एका वाय-फाय नेटवकवरील सर्व डीव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अ‍ॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"ब्लूटूथ सेटिंग्ज अॅक्सेस करा"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"स्थानिक ब्लूटूथ टीव्ही कॉंफिगर करण्यासाठी आणि दूरस्थ डीव्हाइससह शोधण्यासाठी आणि जोडण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX कनेक्ट करा आणि त्यावरून डिस्कनेक्ट करा"</string>
- <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX स्थिती बदला"</string>
- <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅबलेट कनेक्ट करण्यास आणि त्यावरून टॅबलेट डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"WiMAX नेटवर्कवर टॅबलेट कनेक्ट करण्यास आणि त्यावरून टॅबलेट डिस्कनेक्ट करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"WiMAX नेटवर्कवरून टीव्ही कनेक्ट करण्यासाठी आणि त्यावरून टीव्ही डिस्कनेक्ट करण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अॅप ला अनुमती देते."</string>
+ <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ब्लूटूथ डीव्हाइससह जोडा"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"टीव्हीवर ब्लूटूथचे कॉंफिगरेशन पाहण्यासाठी आणि जोडलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"फील्ड जवळील कम्युनिकेशन नियंत्रित करा"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"फील्ड जवळील कम्युनिकेशन (NFC) टॅग, कार्डे आणि वाचक यांच्यासह संवाद करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अ‍ॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेअर वापरा"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची अॅपला अनुमती देते"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -591,11 +591,11 @@
</string-array>
<string name="face_icon_content_description" msgid="4024817159806482191">"चेहरा आयकन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"सिंक सेटिंग्‍ज वाचा"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अ‍ॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अ‍ॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"सिंक चालू आणि बंद करा टॉगल करा"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"खात्यासाठी सिंक सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांच्या अॅप चे सिंक सक्षम करण्यासाठी वापरले जाऊ शकते."</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"खात्यासाठी सिंक सेटिंग्ज सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांच्या अ‍ॅप चे सिंक सक्षम करण्यासाठी वापरले जाऊ शकते."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"सिंक आकडेवारी वाचा"</string>
- <string name="permdesc_readSyncStats" msgid="1510143761757606156">"सिंक इव्हेंटचा इतिहास आणि किती डेटाचे सिंक केले आहे यासह, खात्याची सिंक स्थिती वाचण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_readSyncStats" msgid="1510143761757606156">"सिंक इव्हेंटचा इतिहास आणि किती डेटाचे सिंक केले आहे यासह, खात्याची सिंक स्थिती वाचण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_sdcardRead" msgid="1438933556581438863">"तुमच्या शेअर केलेल्या स्टोरेजचे आशय वाचते"</string>
<string name="permdesc_sdcardRead" msgid="1804941689051236391">"अॅपला तुमच्या शेअर केलेल्या स्टोरेजचे आशय वाचण्याची अनुमती देते."</string>
<string name="permlab_sdcardWrite" msgid="9220937740184960897">"तुमच्या शेअर केलेल्या स्टोरेजच्या आशयांमध्ये सुधारणा करा किंवा हटवा"</string>
@@ -607,17 +607,17 @@
<string name="permlab_register_call_provider" msgid="108102120289029841">"नवीन टेलिकॉम कनेक्शनची नोंदणी करा"</string>
<string name="permdesc_register_call_provider" msgid="7034310263521081388">"नवीन टेलिकॉम कनेक्शनची नोंदणी करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_connection_manager" msgid="1116193254522105375">"टेलिकॉम कनेक्शन व्यवस्थापित करा"</string>
- <string name="permdesc_connection_manager" msgid="5925480810356483565">"टेलिकॉम कनेक्शन व्यवस्थापित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_connection_manager" msgid="5925480810356483565">"टेलिकॉम कनेक्शन व्यवस्थापित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_bind_incall_service" msgid="6773648341975287125">"कॉल-मधील स्‍क्रीनशी परस्‍परसंवाद करा"</string>
<string name="permdesc_bind_incall_service" msgid="8343471381323215005">"वापरकर्ता कॉल-मधील स्‍क्रीन केव्‍हा आणि कशी पाहतो ते नियंत्रित करण्‍याची अ‍ॅपला अनुमती देते."</string>
<string name="permlab_bind_connection_service" msgid="3557341439297014940">"टेलिफोनी सेवांशी परस्परसंवाद साधा"</string>
- <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"कॉल करण्यासाठी/घेण्यासाठी टेलिफोनी सेवांशी परस्परसंवाद साधण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"कॉल करण्यासाठी/घेण्यासाठी टेलिफोनी सेवांशी परस्परसंवाद साधण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_control_incall_experience" msgid="9061024437607777619">"एक कॉल-मधील वापरकर्ता अनुभव प्रदान करा"</string>
<string name="permdesc_control_incall_experience" msgid="915159066039828124">"अ‍ॅप्सला कॉल-मधील वापरकर्ता अनुभव प्रदान करण्‍याची अनुमती देते."</string>
<string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"ऐतिहासिक नेटवर्क वापर वाचा"</string>
<string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"विशिष्ट नेटवर्क आणि अ‍ॅप्सकरिता ऐतिहासिक नेटवर्क वापराचे वाचन करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"नेटवर्क धोरण व्यवस्थापित करा"</string>
- <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"नेटवर्क धोरणे व्यवस्थापित करण्यासाठी आणि अॅप-विशिष्ट नियम परिभाषित करण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"नेटवर्क धोरणे व्यवस्थापित करण्यासाठी आणि अ‍ॅप-विशिष्ट नियम परिभाषित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क वापर हिशोब सुधारित करा"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"अॅप्स वर नेटवर्क वापराचा हिशोब कसा घेतला जातो हे सुधारित करण्यासाठी अॅप्स ला अनुमती देते. सामान्य अॅप्सद्वारे वापरण्यासाठी नाही."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"प्रवेश सूचना"</string>
@@ -633,7 +633,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"नेटवर्क स्‍थितींवरील निरीक्षणांसाठी ऐका"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"अनु्प्रयोगाला नेटवर्क स्‍थितींवरील निरीक्षणे ऐकण्‍यासाठी अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची कॅलिब्रेशन प्राचले सुधारित करण्यासाठी अॅप ला अनुमती देते. सामान्य अॅप्स साठी कधीही आवश्यक नसते."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"स्पर्श स्क्रीनची कॅलिब्रेशन प्राचले सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. सामान्य अ‍ॅप्स साठी कधीही आवश्यक नसते."</string>
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM प्रमाणपत्रे अॅक्सेस करा"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"DRM प्रमाणपत्रांची तरतूद करण्यासाठी आणि वापरण्यासाठी अनुप्रयोगास अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यकता नसते."</string>
<string name="permlab_handoverStatus" msgid="7820353257219300883">"Android बीम स्थानांतरण स्थिती प्राप्त करा"</string>
@@ -672,7 +672,7 @@
<string name="policylab_expirePassword" msgid="5610055012328825874">"स्क्रीन लॉक पासवर्ड कालबाह्यता सेट करा"</string>
<string name="policydesc_expirePassword" msgid="5367525762204416046">"लॉक-स्क्रीन पासवर्ड किती वारंवार बदलणे आवश्यक आहे ते बदला."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"स्टोरेज एंक्रिप्शन सेट करा"</string>
- <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string>
+ <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अ‍ॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"कॅमेरे अक्षम करा"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"सर्व डिव्हाइस कॅमेर्‍यांचा वापर प्रतिबंधित करा."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"काही स्क्रीन लॉक वैशिष्‍ट्ये अक्षम करा"</string>
@@ -897,7 +897,7 @@
<string name="granularity_label_link" msgid="5815508880782488267">"लिंक"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"रेखा"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"फॅक्टरी चाचणी अयशस्वी"</string>
- <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अॅप मध्ये इंस्टॉल केलेल्या पॅकेजसाठी समर्थित आहे."</string>
+ <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST क्रिया फक्त /सिस्टम/अ‍ॅप मध्ये इंस्टॉल केलेल्या पॅकेजसाठी समर्थित आहे."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST क्रिया प्रदान करणारे कोणतेही पॅकेज आढळले नाही."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"रीबूट करा"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\"<xliff:g id="TITLE">%s</xliff:g>\" वरील पृष्ठ हे म्हणते:"</string>
@@ -928,17 +928,17 @@
<string name="autofill_area" msgid="3547409050889952423">"क्षेत्र"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"अमिरात"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"तुमचे वेब बुकमार्क आणि इतिहास वाचा"</string>
- <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अ‍ॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"वेब बुकमार्क आणि इतिहास लिहा"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अ‍ॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"तुमच्या टीव्हीवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅपला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅपला अनुमती देऊ शकते. टीप: या परवानगीची अंमलबजावणी वेब ब्राउझिंग क्षमता असलेल्या तृतीय-पक्ष ब्राउझरद्वारे किंवा इतर अॅप्लिकेशनद्वारे केली जाऊ शकत नाही."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अ‍ॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"अलार्म सेट करा"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"इंस्टॉल केलेल्या अलार्म घड्याळ अॅपमध्ये अलार्म सेट करण्यासाठी अॅपला अनुमती देते. काही अलार्म घड्याळ अॅप्समध्ये हे वैशिष्ट्य नसू शकते."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"व्हॉइसमेल जोडा"</string>
- <string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये मेसेज जोडण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_addVoicemail" msgid="6604508651428252437">"आपल्या व्हॉइसमेल इनबॉक्समध्ये मेसेज जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउझर भौगोलिक स्थान परवानग्या सुधारित करा"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ब्राउझरच्या भौगोलिक स्थान परवानग्या सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स यादृच्छिक वेबसाइटवर स्थान माहिती पाठविण्यास अनुमती देण्यासाठी याचा वापर करू शकतात."</string>
<string name="save_password_message" msgid="767344687139195790">"ब्राउझरने हा पासवर्ड लक्षात ठेवावा असे तुम्ही इच्छिता?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"आत्ता नाही"</string>
<string name="save_password_remember" msgid="6491879678996749466">"लक्षात ठेवा"</string>
@@ -1083,25 +1083,25 @@
<string name="deleteText" msgid="6979668428458199034">"हटवा"</string>
<string name="inputMethod" msgid="1653630062304567879">"इनपुट पद्धत"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"मजकूर क्रिया"</string>
- <string name="email" msgid="4560673117055050403">"ईमेल"</string>
+ <string name="email" msgid="4560673117055050403">"ईमेल करा"</string>
<string name="email_desc" msgid="3638665569546416795">"निवडलेल्या अॅड्रेसवर ईमेल करा"</string>
<string name="dial" msgid="1253998302767701559">"कॉल करा"</string>
<string name="dial_desc" msgid="6573723404985517250">"निवडलेल्या फोन नंबरवर कॉल करा"</string>
- <string name="map" msgid="5441053548030107189">"नकाशा"</string>
+ <string name="map" msgid="5441053548030107189">"नकाशा उघडा"</string>
<string name="map_desc" msgid="1836995341943772348">"निवडलेला पत्ता शोधा"</string>
<string name="browse" msgid="1245903488306147205">"उघडा"</string>
<string name="browse_desc" msgid="8220976549618935044">"निवडलेली URL उघडा"</string>
- <string name="sms" msgid="4560537514610063430">"मेसेज"</string>
+ <string name="sms" msgid="4560537514610063430">"मेसेज करा"</string>
<string name="sms_desc" msgid="7526588350969638809">"निवडलेल्या फोन नंबरवर एसएमएस करा"</string>
<string name="add_contact" msgid="7867066569670597203">"जोडा"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"संपर्कांमध्ये जोडा"</string>
- <string name="view_calendar" msgid="979609872939597838">"पहा"</string>
+ <string name="view_calendar" msgid="979609872939597838">"पाहा"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"निवडलेली वेळ कॅलेंडरमध्ये पाहा"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"शेड्यूल"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"निवडलेल्या वेळेसाठी इव्हेंट शेड्यूल करा"</string>
- <string name="view_flight" msgid="7691640491425680214">"ट्रॅक"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"शेड्युल करा"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"निवडलेल्या वेळेसाठी इव्हेंट शेड्युल करा"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ट्रॅक करा"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"निवडलेले विमान ट्रॅक करा"</string>
- <string name="translate" msgid="9218619809342576858">"भाषांतर"</string>
+ <string name="translate" msgid="9218619809342576858">"भाषांतर करा"</string>
<string name="translate_desc" msgid="4502367770068777202">"निवडलेल्या मजकुराचे भाषांतर करा"</string>
<string name="define" msgid="7394820043869954211">"व्याख्या सांगा"</string>
<string name="define_desc" msgid="7910883642444919726">"निवडलेल्या मजकुराची व्याख्या सांगा"</string>
@@ -1143,18 +1143,18 @@
<string name="use_a_different_app" msgid="8134926230585710243">"एक भिन्न अ‍ॅप वापरा"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"डाउनलोड केलेल्या सिस्टम सेटिंग्ज &gt; Apps &gt; मधील डीफॉल्ट साफ करा."</string>
<string name="chooseActivity" msgid="7486876147751803333">"क्रिया निवडा"</string>
- <string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिव्हाइससाठी अॅप निवडा"</string>
+ <string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिव्हाइससाठी अ‍ॅप निवडा"</string>
<string name="noApplications" msgid="2991814273936504689">"कोणतेही अॅप्स ही क्रिया करू शकत नाहीत."</string>
<string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> थांबला आहे"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> थांबली आहे"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> थांबतो"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> थांबते"</string>
- <string name="aerr_restart" msgid="7581308074153624475">"अॅप पुन्हा उघडा"</string>
+ <string name="aerr_restart" msgid="7581308074153624475">"अ‍ॅप पुन्हा उघडा"</string>
<string name="aerr_report" msgid="5371800241488400617">"अभिप्राय पाठवा"</string>
<string name="aerr_close" msgid="2991640326563991340">"बंद करा"</string>
<string name="aerr_mute" msgid="1974781923723235953">"डिव्हाइस रीस्टार्ट होईपर्यंत म्यूट करा"</string>
<string name="aerr_wait" msgid="3199956902437040261">"प्रतीक्षा करा"</string>
- <string name="aerr_close_app" msgid="3269334853724920302">"अॅप बंद करा"</string>
+ <string name="aerr_close_app" msgid="3269334853724920302">"अ‍ॅप बंद करा"</string>
<string name="anr_title" msgid="4351948481459135709"></string>
<string name="anr_activity_application" msgid="8493290105678066167">"<xliff:g id="APPLICATION">%2$s</xliff:g> प्रतिसाद देत नाही"</string>
<string name="anr_activity_process" msgid="1622382268908620314">"<xliff:g id="ACTIVITY">%1$s</xliff:g> प्रतिसाद देत नाही"</string>
@@ -1164,7 +1164,7 @@
<string name="report" msgid="4060218260984795706">"अहवाल द्या"</string>
<string name="wait" msgid="7147118217226317732">"प्रतीक्षा करा"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"पृष्ठ प्रतिसाद न देणारे झाले आहे.\n\nतुम्ही हे बंद करू इच्छिता?"</string>
- <string name="launch_warning_title" msgid="1547997780506713581">"अॅप पुनर्निर्देशित केला"</string>
+ <string name="launch_warning_title" msgid="1547997780506713581">"अ‍ॅप पुनर्निर्देशित केला"</string>
<string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता चालत आहे."</string>
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> मूळतः लाँच केले."</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
@@ -1175,7 +1175,7 @@
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे Android OS च्या विसंगत आवृत्तीसाठी तयार केले होते आणि ते अनपेक्षित पद्धतीने काम करू शकते. अॅपची अपडेट केलेली आवृत्ती उपलब्ध असू शकते."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"नेहमी दर्शवा"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"अपडेट आहे का ते तपासा"</string>
- <string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
+ <string name="smv_application" msgid="3307209192155442829">"अ‍ॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
<string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> प्रक्रियेने तिच्या स्वतः-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले."</string>
<string name="android_upgrading_title" product="default" msgid="7513829952443484438">"फोन अपडेट होत आहे…"</string>
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टॅबलेट अपडेट होत आहे…"</string>
@@ -1186,7 +1186,7 @@
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
<string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"सिस्‍टम अपडेट संपत आहे…"</string>
<string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> श्रेणीसुधारित करत आहे…"</string>
- <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
+ <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अ‍ॅप ऑप्टिमाइझ करत आहे."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"अॅप्स प्रारंभ करत आहे."</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"बूट समाप्त होत आहे."</string>
@@ -1288,7 +1288,7 @@
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर फोन कनेक्ट केलेला असताना तो वाय-फाय वरून तात्पुरता डिस्कनेक्ट केला जाईल"</string>
<string name="select_character" msgid="3365550120617701745">"वर्ण घाला"</string>
<string name="sms_control_title" msgid="7296612781128917719">"SMS मेसेज पाठवत आहे"</string>
- <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; मोठ्या संख्येने SMS मेसेज पाठवत आहे. तुम्ही या अॅप ला मेसेज पाठविणे सुरु ठेवण्याची अनुमती देऊ इच्छिता?"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; मोठ्या संख्येने SMS मेसेज पाठवत आहे. तुम्ही या अ‍ॅप ला मेसेज पाठविणे सुरु ठेवण्याची अनुमती देऊ इच्छिता?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"अनुमती द्या"</string>
<string name="sms_control_no" msgid="625438561395534982">"नकार द्या"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; हा &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;वर एक मेसेज पाठवू इच्छितो."</string>
@@ -1445,7 +1445,7 @@
<string name="no_file_chosen" msgid="6363648562170759465">"फाईल निवडली नाही"</string>
<string name="reset" msgid="2448168080964209908">"रीसेट करा"</string>
<string name="submit" msgid="1602335572089911941">"सबमिट करा"</string>
- <string name="car_mode_disable_notification_title" msgid="5704265646471239078">"ड्रायव्हिंग अॅप चालू आहे"</string>
+ <string name="car_mode_disable_notification_title" msgid="5704265646471239078">"ड्रायव्हिंग अ‍ॅप चालू आहे"</string>
<string name="car_mode_disable_notification_message" msgid="7647248420931129377">"ड्रायव्हिंग अॅपमधून बाहेर पाडण्यासाठी टॅप करा."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
<string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string>
@@ -1774,7 +1774,7 @@
<string name="package_deleted_device_owner" msgid="2307122077550236438">"आपल्या प्रशासकाने हटवले"</string>
<string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"बॅटरी लाइफ वाढवण्‍यासाठी, बॅटरी सेव्‍हर काही डिव्‍हाइस वैशिष्‍ट्ये बंद करते आणि अॅप्‍सना प्रतिबंधित करते. "<annotation id="url">"अधिक जाणून घ्‍या"</annotation></string>
<string name="battery_saver_description" msgid="769989536172631582">"बॅटरी लाइफ वाढवण्‍यासाठी, बॅटरी सेव्‍हर काही वैशिष्‍ट्ये बंद करते आणि अॅप्‍स प्रतिबंधित करते."</string>
- <string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. तुम्ही सध्या वापरत असलेला अॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, तुम्ही इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अ‍ॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. तुम्ही सध्या वापरत असलेला अ‍ॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, तुम्ही इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"डेटा बचतकर्ता चालू करायचा?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"चालू करा"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
@@ -1861,16 +1861,16 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
- <string name="app_suspended_title" msgid="2075071241147969611">"अॅप उपलब्ध नाही"</string>
+ <string name="app_suspended_title" msgid="2075071241147969611">"अ‍ॅप उपलब्ध नाही"</string>
<string name="app_suspended_default_message" msgid="123166680425711887">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> आत्ता उपलब्ध नाही. हे <xliff:g id="APP_NAME_1">%2$s</xliff:g> कडून व्यवस्थापित केले जाते."</string>
<string name="app_suspended_more_details" msgid="1131804827776778187">"अधिक जाणून घ्या"</string>
<string name="work_mode_off_title" msgid="1118691887588435530">"कार्य प्रोफाइल चालू ठेवायची?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"तुमची कार्य अ‍ॅप्स, सूचना, डेटा आणि अन्य कार्य प्रोफाइल वैशिष्ट्ये चालू केली जातील"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"चालू करा"</string>
- <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अ‍ॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
<string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"अपडेट आहे का ते तपासा"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"आपल्याकडे नवीन मेसेज आहेत"</string>
- <string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्‍यासाठी SMS अॅप उघडा"</string>
+ <string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्‍यासाठी SMS अ‍ॅप उघडा"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"काही कार्यक्षमता मर्यादित असू शकतात"</string>
<string name="user_encrypted_message" msgid="4923292604515744267">"अनलॉक करण्यासाठी टॅप करा"</string>
<string name="user_encrypted_detail" msgid="5708447464349420392">"वापरकर्ता डेटा लॉक केला"</string>
@@ -1880,7 +1880,7 @@
<string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"फायली पाहण्यासाठी टॅप करा"</string>
<string name="pin_target" msgid="3052256031352291362">"पिन"</string>
<string name="unpin_target" msgid="3556545602439143442">"अनपिन करा"</string>
- <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
+ <string name="app_info" msgid="6856026610594615344">"अ‍ॅप माहिती"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ करत आहे..."</string>
<string name="demo_restarting_message" msgid="952118052531642451">"डिव्हाइस रीसेट करत आहे..."</string>
@@ -1945,13 +1945,13 @@
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="4860853725206702336">"अ‍ॅपची आवृत्ती डाउनग्रेड केली, किंवा ती या शॉर्टकटशी कंपॅटिबल नाही"</string>
- <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्‍टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्‍टोअर करू शकलो नाही"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्‍टोअर करू शकलो नाही"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अ‍ॅप बॅकअप आणि रिस्‍टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्‍टोअर करू शकलो नाही"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अ‍ॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्‍टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट रिस्‍टोअर करू शकलो नाही"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद केलेला आहे"</string>
<string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"अनइंस्टॉल करा"</string>
<string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"तरीही उघडा"</string>
- <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अॅप आढळला"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अ‍ॅप आढळला"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवायचे आहेत"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"संपादित करा"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"कॉल आणि सूचनांवर व्हायब्रेट होईल"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 39afd744c415..a3da75ea0ed3 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1083,24 +1083,24 @@
<string name="deleteText" msgid="6979668428458199034">"ဖျက်ရန်"</string>
<string name="inputMethod" msgid="1653630062304567879">"ထည့်သွင်းရန်နည်းလမ်း"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"စာတို လုပ်ဆောင်ချက်"</string>
- <string name="email" msgid="4560673117055050403">"အီးမေးလ်"</string>
+ <string name="email" msgid="4560673117055050403">"အီးမေးလ်ပို့ရန်"</string>
<string name="email_desc" msgid="3638665569546416795">"ရွေးထားသည့် လိပ်စာသို့ အီးမေးလ်ပို့ရန်"</string>
<string name="dial" msgid="1253998302767701559">"ခေါ်ဆိုရန်"</string>
<string name="dial_desc" msgid="6573723404985517250">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို ခေါ်ရန်"</string>
- <string name="map" msgid="5441053548030107189">"မြေပုံ"</string>
+ <string name="map" msgid="5441053548030107189">"မြေပုံဖွင့်ရန်"</string>
<string name="map_desc" msgid="1836995341943772348">"ရွေးထားသည့် လိပ်စာကို ရှာဖွေရန်"</string>
<string name="browse" msgid="1245903488306147205">"ဖွင့်ရန်"</string>
<string name="browse_desc" msgid="8220976549618935044">"ရွေးထားသည့် URL ကို ဖွင့်ရန်"</string>
- <string name="sms" msgid="4560537514610063430">"SMS"</string>
+ <string name="sms" msgid="4560537514610063430">"SMS ပို့ရန်"</string>
<string name="sms_desc" msgid="7526588350969638809">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို မက်ဆေ့ဂျ်ပို့ရန်"</string>
<string name="add_contact" msgid="7867066569670597203">"ထည့်ရန်"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"အဆက်အသွယ်များသို့ ထည့်ရန်"</string>
<string name="view_calendar" msgid="979609872939597838">"ကြည့်ရန်"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ရွေးထားသည့် အချိန်ကို ပြက္ခဒိန်တွင် ကြည့်ရန်"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"အချိန်ဇယား"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"အစီအစဉ်ထည့်ရန်"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ရွေးထားသည့်အချိန်အတွက် အစီအစဉ်ပြုလုပ်ရန်"</string>
<string name="view_flight" msgid="7691640491425680214">"ရှာရန်"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"ရွေးထားသည့် လေယာဉ်ခရီးစဉ်ကို ရှာဖွေရန်"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"ရွေးထားသည့် လေယာဉ်ခရီးစဉ်ကို ရှာရန်"</string>
<string name="translate" msgid="9218619809342576858">"ဘာသာပြန်ရန်"</string>
<string name="translate_desc" msgid="4502367770068777202">"ရွေးထားသောစာသားကို ဘာသာပြန်ရန်"</string>
<string name="define" msgid="7394820043869954211">"အဓိပ္ပာယ်ဖွင့်ဆိုရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index f606d77f28a9..30cd600f734b 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1265,7 +1265,7 @@
</string-array>
<string name="network_switch_type_name_unknown" msgid="4552612897806660656">"en ukjent nettverkstype"</string>
<string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan ikke koble til Wi-Fi"</string>
- <string name="wifi_watchdog_network_disabled_detailed" msgid="4917472096696322767">" har en dårlig Internett-tilkobling."</string>
+ <string name="wifi_watchdog_network_disabled_detailed" msgid="4917472096696322767">" har en dårlig internettilkobling."</string>
<string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vil du tillat tilkoblingen?"</string>
<string name="wifi_connect_alert_message" msgid="6451273376815958922">"Appen %1$s vil koble til Wi-Fi-nettverket %2$s"</string>
<string name="wifi_connect_default_application" msgid="7143109390475484319">"En app"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a6bd007564db..2ff9fde39b49 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -247,7 +247,7 @@
<string name="global_action_settings" msgid="1756531602592545966">"Instellingen"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Helpen"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
- <string name="global_action_lockdown" msgid="1099326950891078929">"Afgesloten"</string>
+ <string name="global_action_lockdown" msgid="1099326950891078929">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
<string name="notification_hidden_text" msgid="6351207030447943784">"Nieuwe melding"</string>
<string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"Virtueel toetsenbord"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 1d1f3e7ae784..e3fe527b1afe 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1083,7 +1083,7 @@
<string name="deleteText" msgid="6979668428458199034">"ਮਿਟਾਓ"</string>
<string name="inputMethod" msgid="1653630062304567879">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ਟੈਕਸਟ ਕਿਰਿਆਵਾਂ"</string>
- <string name="email" msgid="4560673117055050403">"ਈਮੇਲ ਕਰੋ"</string>
+ <string name="email" msgid="4560673117055050403">"ਈਮੇਲ ਖੋਲ੍ਹੋ"</string>
<string name="email_desc" msgid="3638665569546416795">"ਚੁਣੇ ਹੋਏ ਪਤੇ \'ਤੇ ਈਮੇਲ ਭੇਜੋ"</string>
<string name="dial" msgid="1253998302767701559">"ਕਾਲ ਕਰੋ"</string>
<string name="dial_desc" msgid="6573723404985517250">"ਚੁਣੇ ਗਏ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰੋ"</string>
@@ -1097,11 +1097,11 @@
<string name="add_contact_desc" msgid="4830217847004590345">"ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="view_calendar" msgid="979609872939597838">"ਦੇਖੋ"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ਕੈਲੰਡਰ ਵਿੱਚ ਚੁਣਿਆ ਗਿਆ ਸਮਾਂ ਦੇਖੋ"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ ਬਣਾਓ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ਚੁਣੇ ਗਏ ਸਮੇਂ ਲਈ ਇਵੈਂਟ ਦੀ ਸਮਾਂ-ਸੂਚੀ ਬਣਾਓ"</string>
<string name="view_flight" msgid="7691640491425680214">"ਟਰੈਕ ਕਰੋ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ਚੁਣੀ ਗਈ ਉਡਾਣ ਨੂੰ ਟਰੈਕ ਕਰੋ"</string>
- <string name="translate" msgid="9218619809342576858">"ਅਨੁਵਾਦ"</string>
+ <string name="translate" msgid="9218619809342576858">"ਅਨੁਵਾਦ ਕਰੋ"</string>
<string name="translate_desc" msgid="4502367770068777202">"ਚੋਣਵੀਂ ਲਿਖਤ ਦਾ ਅਨੁਵਾਦ ਕਰੋ"</string>
<string name="define" msgid="7394820043869954211">"ਪਰਿਭਾਸ਼ਾ ਦਿਓ"</string>
<string name="define_desc" msgid="7910883642444919726">"ਚੋਣਵੀਂ ਲਿਖਤ ਦਾ ਅਨੁਵਾਦ ਕਰੋ"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9a70f0b4e4b9..0790e904ec18 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1123,26 +1123,26 @@
<string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
<string name="inputMethod" msgid="1653630062304567879">"Način vnosa"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Besedilna dejanja"</string>
- <string name="email" msgid="4560673117055050403">"E-pošta"</string>
+ <string name="email" msgid="4560673117055050403">"Pošlji e-pošto"</string>
<string name="email_desc" msgid="3638665569546416795">"Pošlji na izbrani naslov"</string>
<string name="dial" msgid="1253998302767701559">"Pokliči"</string>
<string name="dial_desc" msgid="6573723404985517250">"Pokliči izbrano telefonsko številko"</string>
- <string name="map" msgid="5441053548030107189">"Zemljevid"</string>
+ <string name="map" msgid="5441053548030107189">"Odpri zemljevid"</string>
<string name="map_desc" msgid="1836995341943772348">"Poišči izbrani naslov na zemljevidu"</string>
<string name="browse" msgid="1245903488306147205">"Odpri"</string>
<string name="browse_desc" msgid="8220976549618935044">"Odpri izbrani URL"</string>
- <string name="sms" msgid="4560537514610063430">"Sporočilo"</string>
+ <string name="sms" msgid="4560537514610063430">"Pošlji SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Pošlji sporočilo na izbrano telefonsko številko"</string>
<string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Dodaj med stike"</string>
- <string name="view_calendar" msgid="979609872939597838">"Ogled"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Ogled izbranega časa v koledarju"</string>
+ <string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži izbrani čas v koledarju"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Dodaj na razpored"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Razporedi dogodek na izbrano uro"</string>
- <string name="view_flight" msgid="7691640491425680214">"Sledenje"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Sledi"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Sledi izbranemu letu"</string>
<string name="translate" msgid="9218619809342576858">"Prevedi"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Prevod izbranega besedila"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedi izbrano besedilo"</string>
<string name="define" msgid="7394820043869954211">"Opredeli"</string>
<string name="define_desc" msgid="7910883642444919726">"Opredeli izbrano besedilo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 25171b835434..51ac513b8885 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1096,10 +1096,10 @@
<string name="add_contact" msgid="7867066569670597203">"Ongeza"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Ongeza kwenye anwani"</string>
<string name="view_calendar" msgid="979609872939597838">"Angalia"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Angalia wakati uliyochagua katika kalenda"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Ratiba"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Angalia wakati uliochagua katika kalenda"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Ratibu"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Ratibu tukio la wakati uliochagua"</string>
- <string name="view_flight" msgid="7691640491425680214">"Toleo"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Fuatilia"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Fuatilia ndege uliyochagua"</string>
<string name="translate" msgid="9218619809342576858">"Tafsiri"</string>
<string name="translate_desc" msgid="4502367770068777202">"Tafsiri maandishi yaliyochaguliwa"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 492bad58842a..f59cfcb427a1 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1091,7 +1091,7 @@
<string name="map_desc" msgid="1836995341943772348">"搵出指定地址"</string>
<string name="browse" msgid="1245903488306147205">"開啟"</string>
<string name="browse_desc" msgid="8220976549618935044">"打開指定網址"</string>
- <string name="sms" msgid="4560537514610063430">"短訊"</string>
+ <string name="sms" msgid="4560537514610063430">"發短訊"</string>
<string name="sms_desc" msgid="7526588350969638809">"Send 短訊去指定電話號碼"</string>
<string name="add_contact" msgid="7867066569670597203">"新增"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"加入通訊錄"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 854582b21340..ab4bd0534025 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -268,6 +268,12 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system default text classifier -->
<flag name="textClassifier" value="0x10000" />
+ <!-- Additional flag from base permission type: this permission will be granted to the
+ wellbeing app, as defined by the OEM. -->
+ <flag name="wellbeing" value="0x20000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the document manager -->
+ <flag name="documenter" value="0x40000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 62ec5c474297..101f92b2097c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3349,6 +3349,13 @@
See android.view.textclassifier.TextClassificationManager.
-->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
+
+ <!-- The package name for the default wellbeing app.
+ This package must be trusted, as it has the permissions to control other applications
+ on the device.
+ Example: "com.android.wellbeing"
+ -->
+ <string name="config_defaultWellbeingPackage" translatable="false"></string>
<!-- The package name for the system's content capture service.
This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a33f6b2fbf9c..165b06f159bc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3323,6 +3323,15 @@
<!-- Notification action name for opening the wifi picker, showing the user all the nearby networks. -->
<string name="wifi_available_action_all_networks">All networks</string>
+ <!-- Notification title for a connection to a app suggested wireless network.-->
+ <string name="wifi_suggestion_title">Connected to Wi\u2011Fi network proposed by <xliff:g id="name" example="App123">%s</xliff:g></string>
+ <!-- Notification content for a connection to a app suggested wireless network.-->
+ <string name="wifi_suggestion_content">Do you want to let <xliff:g id="name" example="App123">%s</xliff:g> propose networks for you?</string>
+ <!-- Notification action for allowing app specified in the notification body.-->
+ <string name="wifi_suggestion_action_allow_app">Yes</string>
+ <!-- Notification action for disallowing app specified in the notification body.-->
+ <string name="wifi_suggestion_action_disallow_app">No</string>
+
<!--Notification title for Wi-Fi Wake onboarding. This is displayed the first time a user disables Wi-Fi with the feature enabled. -->
<string name="wifi_wakeup_onboarding_title">Wi\u2011Fi will turn on automatically</string>
<!--Notification subtext for Wi-Fi Wake onboarding.-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 82a679e14534..d573c09a6538 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1988,6 +1988,10 @@
<java-symbol type="string" name="wifi_available_content_failed_to_connect" />
<java-symbol type="string" name="wifi_available_action_connect" />
<java-symbol type="string" name="wifi_available_action_all_networks" />
+ <java-symbol type="string" name="wifi_suggestion_title" />
+ <java-symbol type="string" name="wifi_suggestion_content" />
+ <java-symbol type="string" name="wifi_suggestion_action_allow_app" />
+ <java-symbol type="string" name="wifi_suggestion_action_disallow_app" />
<java-symbol type="string" name="wifi_wakeup_onboarding_title" />
<java-symbol type="string" name="wifi_wakeup_onboarding_subtext" />
<java-symbol type="string" name="wifi_wakeup_onboarding_action_disable" />
@@ -3265,6 +3269,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
+ <java-symbol type="string" name="config_defaultWellbeingPackage" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
<java-symbol type="string" name="config_defaultAugmentedAutofillService" />
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
index ac8d7ab98344..3bca5fbab486 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceService.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
@@ -25,9 +25,25 @@ import android.content.Intent;
public class BinderWorkSourceService extends Service {
private final IBinderWorkSourceService.Stub mBinder =
new IBinderWorkSourceService.Stub() {
+ public int getBinderCallingUid() {
+ return Binder.getCallingUid();
+ }
+
public int getIncomingWorkSourceUid() {
return Binder.getCallingWorkSourceUid();
}
+
+ public int getThreadLocalWorkSourceUid() {
+ return ThreadLocalWorkSource.getUid();
+ }
+
+ public void setWorkSourceProvider(int uid) {
+ Binder.setWorkSourceProvider(() -> uid);
+ }
+
+ public void clearWorkSourceProvider() {
+ Binder.setWorkSourceProvider(Binder::getCallingUid);
+ }
};
public IBinder onBind(Intent intent) {
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index ec178031cc45..d1dbd3ccba33 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -163,4 +163,24 @@ public class BinderWorkSourceTest {
// Initial work source restored.
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
+
+ @Test
+ public void workSourceProvider_default() throws Exception {
+ Binder.clearCallingWorkSource();
+ mService.clearWorkSourceProvider();
+ assertEquals(Process.myUid(), mService.getThreadLocalWorkSourceUid());
+ }
+
+ @Test
+ public void workSourceProvider_customProvider() throws Exception {
+ Binder.clearCallingWorkSource();
+ mService.clearWorkSourceProvider();
+ // Calling uid should not be used.
+ mService.setWorkSourceProvider(SECOND_UID);
+ try {
+ assertEquals(SECOND_UID, mService.getThreadLocalWorkSourceUid());
+ } finally {
+ mService.clearWorkSourceProvider();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
index 05d4e829be0a..93224003e9a5 100644
--- a/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -18,4 +18,8 @@ package android.os;
interface IBinderWorkSourceService {
int getIncomingWorkSourceUid();
+ int getBinderCallingUid();
+ int getThreadLocalWorkSourceUid();
+ void setWorkSourceProvider(int uid);
+ void clearWorkSourceProvider();
}
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index da17b56bc0fa..a828f4418515 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -16,13 +16,32 @@
package android.os;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.After;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
public class PowerManagerTest extends AndroidTestCase {
private PowerManager mPm;
+ private UiDevice mUiDevice;
+ private Executor mExec = Executors.newSingleThreadExecutor();
+ @Mock
+ private PowerManager.ThermalStatusCallback mCallback;
+ private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
/**
* Setup any common data for the upcoming tests.
@@ -30,7 +49,18 @@ public class PowerManagerTest extends AndroidTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mUiDevice.executeShellCommand("cmd thermalservice override-status 0");
+ }
+
+ /**
+ * Reset data for the upcoming tests.
+ */
+ @After
+ public void tearDown() throws Exception {
+ mUiDevice.executeShellCommand("cmd thermalservice reset");
}
/**
@@ -137,4 +167,35 @@ public class PowerManagerTest extends AndroidTestCase {
// TODO: Threaded test (needs handler) to make sure timed wakelocks work too
}
+
+ @Test
+ public void testGetThermalStatus() throws Exception {
+ int status = 0;
+ assertEquals(status, mPm.getCurrentThermalStatus());
+ status = 3;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + Integer.toString(status));
+ assertEquals(status, mPm.getCurrentThermalStatus());
+ }
+
+ @Test
+ public void testThermalStatusCallback() throws Exception {
+ mPm.registerThermalStatusCallback(mCallback, mExec);
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(0);
+ reset(mCallback);
+ int status = 3;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + Integer.toString(status));
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ reset(mCallback);
+ mPm.unregisterThermalStatusCallback(mCallback);
+ status = 2;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + Integer.toString(status));
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(status);
+
+ }
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 49a7555c2ac4..3a37fb627b26 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -415,6 +415,7 @@ public class SettingsBackupTest {
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
+ Settings.Global.SIGNED_CONFIG_VERSION,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED,
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 8d6fbd563d6c..61ab1526abc4 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -28,7 +28,8 @@ import com.google.common.collect.Lists;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO(b/73862682): Add tests for RecoveryCertPath
+import java.security.cert.CertPath;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class KeyChainSnapshotTest {
@@ -43,35 +44,41 @@ public class KeyChainSnapshotTest {
private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
private static final String KEY_ALIAS = "steph";
private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
+ private static final CertPath CERT_PATH = TestData.getThmCertPath();
@Test
- public void build_setsCounterId() {
+ public void build_setsCounterId() throws Exception {
assertEquals(COUNTER_ID, createKeyChainSnapshot().getCounterId());
}
@Test
- public void build_setsSnapshotVersion() {
+ public void build_setsSnapshotVersion() throws Exception {
assertEquals(SNAPSHOT_VERSION, createKeyChainSnapshot().getSnapshotVersion());
}
@Test
- public void build_setsMaxAttempts() {
+ public void build_setsMaxAttempts() throws Exception {
assertEquals(MAX_ATTEMPTS, createKeyChainSnapshot().getMaxAttempts());
}
@Test
- public void build_setsServerParams() {
+ public void build_setsServerParams() throws Exception {
assertArrayEquals(SERVER_PARAMS, createKeyChainSnapshot().getServerParams());
}
@Test
- public void build_setsRecoveryKeyBlob() {
+ public void build_setsRecoveryKeyBlob() throws Exception {
assertArrayEquals(RECOVERY_KEY_BLOB,
createKeyChainSnapshot().getEncryptedRecoveryKeyBlob());
}
@Test
- public void build_setsKeyChainProtectionParams() {
+ public void build_setsCertPath() throws Exception {
+ assertEquals(CERT_PATH, createKeyChainSnapshot().getTrustedHardwareCertPath());
+ }
+
+ @Test
+ public void build_setsKeyChainProtectionParams() throws Exception {
KeyChainSnapshot snapshot = createKeyChainSnapshot();
assertEquals(1, snapshot.getKeyChainProtectionParams().size());
@@ -85,7 +92,7 @@ public class KeyChainSnapshotTest {
}
@Test
- public void build_setsWrappedApplicationKeys() {
+ public void build_setsWrappedApplicationKeys() throws Exception {
KeyChainSnapshot snapshot = createKeyChainSnapshot();
assertEquals(1, snapshot.getWrappedApplicationKeys().size());
@@ -95,42 +102,49 @@ public class KeyChainSnapshotTest {
}
@Test
- public void writeToParcel_writesCounterId() {
+ public void writeToParcel_writesCounterId() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(COUNTER_ID, snapshot.getCounterId());
}
@Test
- public void writeToParcel_writesSnapshotVersion() {
+ public void writeToParcel_writesSnapshotVersion() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(SNAPSHOT_VERSION, snapshot.getSnapshotVersion());
}
@Test
- public void writeToParcel_writesMaxAttempts() {
+ public void writeToParcel_writesMaxAttempts() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(MAX_ATTEMPTS, snapshot.getMaxAttempts());
}
@Test
- public void writeToParcel_writesServerParams() {
+ public void writeToParcel_writesServerParams() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertArrayEquals(SERVER_PARAMS, snapshot.getServerParams());
}
@Test
- public void writeToParcel_writesKeyRecoveryBlob() {
+ public void writeToParcel_writesKeyRecoveryBlob() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertArrayEquals(RECOVERY_KEY_BLOB, snapshot.getEncryptedRecoveryKeyBlob());
}
@Test
- public void writeToParcel_writesKeyChainProtectionParams() {
+ public void writeToParcel_writesCertPath() throws Exception {
+ KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+ assertEquals(CERT_PATH, snapshot.getTrustedHardwareCertPath());
+ }
+
+ @Test
+ public void writeToParcel_writesKeyChainProtectionParams() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(1, snapshot.getKeyChainProtectionParams().size());
@@ -144,7 +158,7 @@ public class KeyChainSnapshotTest {
}
@Test
- public void writeToParcel_writesWrappedApplicationKeys() {
+ public void writeToParcel_writesWrappedApplicationKeys() throws Exception {
KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
assertEquals(1, snapshot.getWrappedApplicationKeys().size());
@@ -153,7 +167,7 @@ public class KeyChainSnapshotTest {
assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
}
- private static KeyChainSnapshot createKeyChainSnapshot() {
+ private static KeyChainSnapshot createKeyChainSnapshot() throws Exception {
return new KeyChainSnapshot.Builder()
.setCounterId(COUNTER_ID)
.setSnapshotVersion(SNAPSHOT_VERSION)
@@ -162,6 +176,7 @@ public class KeyChainSnapshotTest {
.setEncryptedRecoveryKeyBlob(RECOVERY_KEY_BLOB)
.setKeyChainProtectionParams(Lists.newArrayList(createKeyChainProtectionParams()))
.setWrappedApplicationKeys(Lists.newArrayList(createWrappedApplicationKey()))
+ .setTrustedHardwareCertPath(CERT_PATH)
.build();
}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
new file mode 100644
index 000000000000..dd8cd8dbd0c6
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.cert.CertificateException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RecoveryCertPathTest {
+
+ @Test
+ public void createRecoveryCertPath_getCertPath_succeeds() throws Exception {
+ RecoveryCertPath recoveryCertPath = RecoveryCertPath.createRecoveryCertPath(
+ TestData.getThmCertPath());
+ assertEquals(TestData.getThmCertPath(), recoveryCertPath.getCertPath());
+ }
+
+ @Test
+ public void getCertPath_throwsIfCannnotDecode() {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeByteArray(new byte[]{0, 1, 2, 3});
+ parcel.setDataPosition(0);
+ RecoveryCertPath recoveryCertPath = RecoveryCertPath.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ try {
+ recoveryCertPath.getCertPath();
+ fail("Did not throw when attempting to decode invalid cert path");
+ } catch (CertificateException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void writeToParcel_writesCertPath() throws Exception {
+ RecoveryCertPath recoveryCertPath =
+ writeToThenReadFromParcel(
+ RecoveryCertPath.createRecoveryCertPath(TestData.getThmCertPath()));
+ assertEquals(TestData.getThmCertPath(), recoveryCertPath.getCertPath());
+ }
+
+ private RecoveryCertPath writeToThenReadFromParcel(RecoveryCertPath recoveryCertPath) {
+ Parcel parcel = Parcel.obtain();
+ recoveryCertPath.writeToParcel(parcel, /*flags=*/ 0);
+ parcel.setDataPosition(0);
+ RecoveryCertPath fromParcel = RecoveryCertPath.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return fromParcel;
+ }
+}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TestData.java b/core/tests/coretests/src/android/security/keystore/recovery/TestData.java
new file mode 100644
index 000000000000..829a92a806b9
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TestData.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.util.Base64;
+
+/** This class provides data for testing purposes. */
+class TestData {
+
+ private static final String THM_CERT_PATH_BASE64 = ""
+ + "MIIIXTCCBRowggMCoAMCAQICEB35ZwzVpI9ssXg9SAehnU0wDQYJKoZIhvcNAQEL"
+ + "BQAwMTEvMC0GA1UEAxMmR29vZ2xlIENsb3VkIEtleSBWYXVsdCBTZXJ2aWNlIFJv"
+ + "b3QgQ0EwHhcNMTgwNTA3MTg1ODEwWhcNMjgwNTA4MTg1ODEwWjA5MTcwNQYDVQQD"
+ + "Ey5Hb29nbGUgQ2xvdWQgS2V5IFZhdWx0IFNlcnZpY2UgSW50ZXJtZWRpYXRlIENB"
+ + "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA73TrvH3j6zEimpcc32tx"
+ + "2iupWwfyzdE5l4Ejc5EBYzx0aZH6b/KDuutwustk0IoyjlGySMBz/21YgWejIm+n"
+ + "duAlpk7WY5kYHp0XWtzdmxZknmWTqugPeNZeiKEjoDmpyIbY6N+f13hQ2RVh+WDT"
+ + "EowQ/i04WBL75chshlIG+3A42g5Qr7DZEKdT9oJQqkntzj0cGyJ5X8BwjeTiJrvY"
+ + "k2Kn/0555/Kpp65G3Rf29VPPU3i67kthAT3SavLBpH03S4WZ+QlfrAiGQziydtz9"
+ + "t7mSk1xefjax5ZWAuJAfCbKfI3VWAcaUr4P57BzmDcSi0jgs1aM3t2BrPfAMRxWv"
+ + "35yDZnrC+HipzkjyDGBfHmFgoglyhc9e/Kj3mSusO0Rq1wguVXKs2hKXRoaGJuHt"
+ + "e3YIwTC1pLznqvolhD1nPoXf8rMzgHRzlc9H8iXsgB1p7975nh5WCPrMDX2eAmYd"
+ + "a0xTMccTeBzIM2ohxQsxlh5rsjXVNU3ihbWkHquzIiwFcAtldP3dMksj0dn/DnYD"
+ + "yokjEgU/z2I216E93x9hmKkEk6Pp7o8t/z6lwMT9FJIuzp7NREnWCSi+e5s2E7FD"
+ + "j6S7xY2zEIUHrmwuuJc0jzJnwdZ+0myinaTmBDvBXR5cU1cmEAZoheCAoRv9Z/6o"
+ + "ASczLF0C4uuVfA5GXcAm14cCAwEAAaMmMCQwDgYDVR0PAQH/BAQDAgGGMBIGA1Ud"
+ + "EwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcNAQELBQADggIBAEPht79yQm8woQbPB1Bs"
+ + "eotkzJtTWTO9fnIWwNiRfQ3vJFXf69ghE77wUS13Ez3FlgNPj0Qxmg5ouE0d2yYV"
+ + "4AUrXnEGZELcyN2XHRXyNK0zXgnr3x6eZyY7QfgGKJgkyja5TS6ZPWGyaLKhClS0"
+ + "AYZSzWJtz0+AkGCdTbmyy7ShdXJ+GfnmssbndZA62VhcjeQmHsDq7V3PKAsp4/B9"
+ + "PzcnTrgkUFNnP1F1pr7JpUUX3xyRFy6gjIrUx1fcOFRxFYPWGLLMZ6P41rafm+M/"
+ + "CbBNr5CY7NrZjr34jLqWycfYes49o9OK44X/wPrxj0Sjg+VrW21+AJ9vrM7DS5hE"
+ + "QX1lDbDtQGkk3N1vgCTo6xt9LXsEu4xUT5bk7YAfpJqM0ltDFPwYAGCbjSkVT/M5"
+ + "JVZkKiUW668Us67x8yZc/5bxbvTA+5xrYhak/VYIBY6qub4J+bKwadw6uBgxnq4P"
+ + "hwgwjfaoJy9YAXCswjCtaE9GwkVmRnJE9vFjJ33IGf37hFTYEHBFy4FomVmQwRFZ"
+ + "TIe7tkKDq9i18F7lzBPJPO6wEG8bxi4csatrjcVHR9erpY5u6ebtkKG8qsan9qzh"
+ + "iWAgSytiT++HejZeoQ+RRgQWjupjdDo5/0oSdQqvaN8Ah6C2J+ecCZ12Lu0FwF+t"
+ + "t9Ie3pF6W8TzxzuMdFWq+afvMIIDOzCCASOgAwIBAgIRAOTj/iNQb6/Qit7zAW9n"
+ + "cL0wDQYJKoZIhvcNAQELBQAwOTE3MDUGA1UEAxMuR29vZ2xlIENsb3VkIEtleSBW"
+ + "YXVsdCBTZXJ2aWNlIEludGVybWVkaWF0ZSBDQTAeFw0xODA1MDcyMjE4MTFaFw0y"
+ + "MzA1MDgyMjE4MTFaMDIxMDAuBgNVBAMTJ0dvb2dsZSBDbG91ZCBLZXkgVmF1bHQg"
+ + "U2VydmljZSBFbmRwb2ludDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI4MEUp5"
+ + "IHwATNfpBuJYIUX6JMsHZt798YO0JlWYy6nVVa1lxf9c+xxONJh+T5aio370RlIE"
+ + "uiq5R7vCHt0VGsCjEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB"
+ + "AGf6QU58lU+gGzy8hnp0suR/ixC++CExtf39pDHkdfU/e3ui4ROR+pjQ5F7okDFW"
+ + "eKSCNcJZ7tyXMJ9g7/I0qVY8Bj/gRnlVokdl/wD5PiL9GIzqWfnHNe3T+xrAAAgO"
+ + "D0bEmjgwNYmekfUIYQczd04d7ZMGnmAkpVH/0O2mf9q5x9fMlbKuAygUqQ/gmnlg"
+ + "xKfl9DSRWi4oMBOqlKlCRP1XAh3anu92+M/EhsFbyc07CWZY0SByX5M/cHVMLhUX"
+ + "jZHvcYLyOmJWJmXznidgyNeIR6t9yDB55iCt7WSn3qMY+9vA9ELzt8jYpBNaKc0G"
+ + "bWQkRzYWegkf4kMis98eQ3SnAKbRz6669nmuAdxKs9/LK6BlFOFw1xvsTRQ96dBa"
+ + "oiX2XGhou+Im0Td/AMs0Aigz2N+Ujq/yW//35GZQfdGGIYtFbkcltStygjIJyAM1"
+ + "pBhyBBkJhOhRpO4fXh98aq8H5J7R9i5A9WpnDstAxPxcNCDWn0O/WxhPvVZkFTpi"
+ + "NXh9dnlJ/kZe+j+z5ZMaxW435drLPx2AQKjXA9GgGrFPltTUyGycmEGtuxLvSnm/"
+ + "zPlmk5FUk7x2wEr0+bZ3cx0JHHgAtgXpe0jkDi8Bw8O3X7mUOjxVhYU6auiYJezW"
+ + "9LGmweaKwYvS04UCWOReolUVexob9LI/VX1JrrwD3s7k";
+
+ static CertPath getThmCertPath() {
+ try {
+ return decodeCertPath(THM_CERT_PATH_BASE64);
+ } catch (Exception e) {
+ // Should never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static CertPath decodeCertPath(String base64CertPath) throws Exception {
+ byte[] certPathBytes = Base64.getDecoder().decode(base64CertPath);
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return certFactory.generateCertPath(new ByteArrayInputStream(certPathBytes), "PkiPath");
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ed80cd7e375c..2ad6028960df 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -19,6 +19,7 @@ package android.view;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
@@ -33,7 +34,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class InsetsControllerTest {
- private InsetsController mController = new InsetsController();
+ private InsetsController mController = new InsetsController(mock(ViewRootImpl.class));
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 5a20ba2522b1..6d0f98433168 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -53,7 +53,7 @@ public class InsetsSourceConsumerTest {
.setName("testSurface")
.build();
mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(),
- () -> mMockTransaction);
+ () -> mMockTransaction, mMockController);
mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 91a54409608a..aaf7312fe0e6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -189,6 +189,7 @@ public class TextClassificationTest {
Instant.ofEpochMilli(946771200000L), // 2000-01-02
ZoneId.of("UTC"));
final String text = "text";
+ final String packageName = "packageName";
final TextClassification.Request reference =
new TextClassification.Request.Builder(text, 0, text.length())
@@ -196,6 +197,7 @@ public class TextClassificationTest {
.setReferenceTime(referenceTime)
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -204,12 +206,13 @@ public class TextClassificationTest {
final TextClassification.Request result =
TextClassification.Request.CREATOR.createFromParcel(parcel);
- assertEquals(text, result.getText());
+ assertEquals(text, result.getText().toString());
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals(referenceTime, result.getReferenceTime());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(referenceTime, result.getReferenceTime());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 75ca769294ce..1dcaed6f6a1e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -69,10 +69,12 @@ public final class TextLanguageTest {
final String bundleKey = "experiment.str";
final Bundle bundle = new Bundle();
bundle.putString(bundleKey, "bundle");
+ final String packageName = "packageName";
final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
.setExtras(bundle)
.build();
+ reference.setCallingPackageName(packageName);
final Parcel parcel = Parcel.obtain();
reference.writeToParcel(parcel, 0);
@@ -81,5 +83,6 @@ public final class TextLanguageTest {
assertEquals(text, result.getText());
assertEquals("bundle", result.getExtras().getString(bundleKey));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f6ec0e6480f2..f022d040246b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -64,7 +64,7 @@ public class TextLinksTest {
public void testParcel() {
final String fullText = "this is just a test";
final TextLinks reference = new TextLinks.Builder(fullText)
- .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
+ .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f), BUNDLE)
.addLink(5, 12, getEntityScores(.8f, .1f, .5f))
.setExtras(BUNDLE)
.build();
@@ -82,6 +82,7 @@ public class TextLinksTest {
assertEquals(1, resultList.get(0).getEntityCount());
assertEquals(TextClassifier.TYPE_OTHER, resultList.get(0).getEntity(0));
assertEquals(1.f, resultList.get(0).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+ assertEquals(BUNDLE_VALUE, resultList.get(0).getExtras().getString(BUNDLE_KEY));
assertEquals(5, resultList.get(1).getStart());
assertEquals(12, resultList.get(1).getEnd());
assertEquals(3, resultList.get(1).getEntityCount());
@@ -96,6 +97,7 @@ public class TextLinksTest {
@Test
public void testParcelOptions() {
+ final String packageName = "packageName";
final TextClassifier.EntityConfig entityConfig = TextClassifier.EntityConfig.create(
Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
Arrays.asList("a", "b", "c"),
@@ -105,6 +107,7 @@ public class TextLinksTest {
.setEntityConfig(entityConfig)
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -119,5 +122,6 @@ public class TextLinksTest {
assertEquals(new HashSet<String>(Arrays.asList("a", "c")),
result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 7ea5108bc3bb..2ea49f7d21be 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -75,11 +75,13 @@ public class TextSelectionTest {
@Test
public void testParcelRequest() {
final String text = "text";
+ final String packageName = "packageName";
final TextSelection.Request reference =
new TextSelection.Request.Builder(text, 0, text.length())
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setExtras(BUNDLE)
.build();
+ reference.setCallingPackageName(packageName);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -87,10 +89,11 @@ public class TextSelectionTest {
parcel.setDataPosition(0);
final TextSelection.Request result = TextSelection.Request.CREATOR.createFromParcel(parcel);
- assertEquals(text, result.getText());
+ assertEquals(text, result.getText().toString());
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+ assertEquals(packageName, result.getCallingPackageName());
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index e2618191235d..97f02cbc27e9 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -57,9 +57,9 @@ public class BinderCallsStatsTest {
bcs.setSamplingInterval(5);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -73,17 +73,17 @@ public class BinderCallsStatsTest {
assertEquals(1, callStatsList.get(0).transactionCode);
// CPU usage is sampled, should not be tracked here.
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 20;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(2, uidEntry.callCount);
assertEquals(1, uidEntry.recordedCallCount);
assertEquals(10, uidEntry.cpuTimeMicros);
assertEquals(1, callStatsList.size());
- callSession = bcs.callStarted(binder, 2);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
assertEquals(3, uidEntry.callCount);
assertEquals(1, uidEntry.recordedCallCount);
@@ -98,9 +98,9 @@ public class BinderCallsStatsTest {
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -116,9 +116,9 @@ public class BinderCallsStatsTest {
assertEquals(binder.getClass(), callStatsList.get(0).binderClass);
assertEquals(1, callStatsList.get(0).transactionCode);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 20;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
assertEquals(2, uidEntry.callCount);
@@ -126,9 +126,9 @@ public class BinderCallsStatsTest {
callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(1, callStatsList.size());
- callSession = bcs.callStarted(binder, 2);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
assertEquals(3, uidEntry.callCount);
@@ -142,7 +142,7 @@ public class BinderCallsStatsTest {
public void testEnableInBetweenCall() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
Binder binder = new Binder();
- bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(0, uidEntries.size());
@@ -153,7 +153,7 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
Binder binder = new Binder();
bcs.callThrewException(null, new IllegalStateException());
- bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(0, uidEntries.size());
@@ -166,17 +166,17 @@ public class BinderCallsStatsTest {
bcs.setSamplingInterval(2);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 1000; // shoud be ignored.
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -203,13 +203,13 @@ public class BinderCallsStatsTest {
bcs.setSamplingInterval(2);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 2 /* another method */);
+ callSession = bcs.callStarted(binder, 2 /* another method */, WORKSOURCE_UID);
bcs.time += 1000; // shoud be ignored.
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
@@ -246,9 +246,9 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new BinderWithGetTransactionName();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList =
bcs.getExportedCallStats();
@@ -261,18 +261,18 @@ public class BinderCallsStatsTest {
bcs.setDetailedTracking(true);
Binder binder = new AnotherBinderWithGetTransactionName();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
Binder binder2 = new BinderWithGetTransactionName();
- callSession = bcs.callStarted(binder2, 1);
+ callSession = bcs.callStarted(binder2, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 2);
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList =
bcs.getExportedCallStats();
@@ -292,9 +292,9 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList =
bcs.getExportedCallStats();
@@ -306,9 +306,9 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList =
new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
@@ -322,13 +322,13 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 50;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList =
new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
@@ -341,13 +341,13 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.elapsedTime += 5;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.elapsedTime += 1;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList =
new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
@@ -368,17 +368,17 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new IllegalStateException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new IllegalStateException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new RuntimeException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
ArrayMap<String, Integer> expected = new ArrayMap<>();
expected.put("java.lang.IllegalStateException", 2);
@@ -391,9 +391,9 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats(null);
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
bcs.setDeviceState(mDeviceState.getReadonlyClient());
@@ -408,8 +408,8 @@ public class BinderCallsStatsTest {
mDeviceState.setCharging(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(0, bcs.getUidEntries().size());
}
@@ -420,8 +420,8 @@ public class BinderCallsStatsTest {
bcs.setDetailedTracking(true);
mDeviceState.setScreenInteractive(false);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -437,8 +437,8 @@ public class BinderCallsStatsTest {
bcs.setDetailedTracking(true);
mDeviceState.setScreenInteractive(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(1, uidEntries.size());
@@ -455,8 +455,8 @@ public class BinderCallsStatsTest {
mDeviceState.setCharging(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(0, bcs.getExportedCallStats().size());
}
@@ -468,8 +468,8 @@ public class BinderCallsStatsTest {
mDeviceState.setCharging(false);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(1, bcs.getExportedCallStats().size());
}
@@ -479,12 +479,12 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.callThrewException(callSession, new IllegalStateException());
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
PrintWriter pw = new PrintWriter(new StringWriter());
- bcs.dump(pw, new HashMap<>(), true);
+ bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), true);
}
@Test
@@ -492,8 +492,8 @@ public class BinderCallsStatsTest {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(false);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(0, bcs.getExportedCallStats().size());
}
@@ -504,10 +504,10 @@ public class BinderCallsStatsTest {
bcs.setDetailedTracking(true);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
bcs.elapsedTime += 20;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
assertEquals(1, bcs.getExportedCallStats().size());
BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
@@ -549,15 +549,15 @@ public class BinderCallsStatsTest {
bcs.setMaxBinderCallStats(2);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 1);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
@@ -574,12 +574,15 @@ public class BinderCallsStatsTest {
bcs.setMaxBinderCallStats(1);
Binder binder = new Binder();
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
- callSession = bcs.callStarted(binder, 2);
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+ // Should use the same overflow entry.
+ callSession = bcs.callStarted(binder, 3, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
assertEquals(2, callStatsList.size());
@@ -590,11 +593,46 @@ public class BinderCallsStatsTest {
assertEquals(CALLING_UID, callStats.callingUid);
callStats = callStatsList.get(1);
- assertEquals(1, callStats.callCount);
+ assertEquals(2, callStats.callCount);
assertEquals("-1", callStats.methodName);
assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
callStats.className);
- assertEquals(CALLING_UID, callStats.callingUid);
+ assertEquals(false , callStats.screenInteractive);
+ assertEquals(-1 , callStats.callingUid);
+ }
+
+ @Test
+ public void testOverflow_oneOverflowEntryPerUid() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(1);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID + 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID + 1);
+
+ // Different uids have different overflow entries.
+ callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID + 2);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID + 2);
+
+ List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
+ assertEquals(3, callStatsList.size());
+
+ BinderCallsStats.ExportedCallStat callStats = callStatsList.get(1);
+ assertEquals(WORKSOURCE_UID + 1, callStats.workSourceUid);
+ assertEquals(1, callStats.callCount);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
+
+ callStats = callStatsList.get(2);
+ assertEquals(WORKSOURCE_UID + 2, callStats.workSourceUid);
+ assertEquals(1, callStats.callCount);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
}
@Test
@@ -620,7 +658,6 @@ public class BinderCallsStatsTest {
class TestBinderCallsStats extends BinderCallsStats {
public int callingUid = CALLING_UID;
- public int workSourceUid = WORKSOURCE_UID;
public long time = 1234;
public long elapsedTime = 0;
@@ -662,11 +699,6 @@ public class BinderCallsStatsTest {
protected int getCallingUid() {
return callingUid;
}
-
- @Override
- protected int getWorkSourceUid() {
- return workSourceUid;
- }
}
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 141948f1d25c..4a2db0a6bb54 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -225,15 +225,18 @@
<library name="android.test.base"
file="/system/framework/android.test.base.impl.jar" />
<library name="android.test.mock"
- file="/system/framework/android.test.mock.impl.jar" />
+ file="/system/framework/android.test.mock.impl.jar"
+ dependency="android.test.base" />
<library name="android.test.runner"
- file="/system/framework/android.test.runner.impl.jar" />
+ file="/system/framework/android.test.runner.impl.jar"
+ dependency="android.test.base:android.test.mock" />
<!-- In BOOT_JARS historically, and now added to legacy applications. -->
<library name="android.hidl.base-V1.0-java"
file="/system/framework/android.hidl.base-V1.0-java.jar" />
<library name="android.hidl.manager-V1.0-java"
- file="/system/framework/android.hidl.manager-V1.0-java.jar" />
+ file="/system/framework/android.hidl.manager-V1.0-java.jar"
+ dependency="android.hidl.base-V1.0-java" />
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f2373446924a..0a2f057ccb69 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -141,6 +141,7 @@ applications that come with the platform
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+ <permission name="android.permission.CLEAR_APP_USER_DATA"/>
</privapp-permissions>
<privapp-permissions package="com.android.permissioncontroller">
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 889d166d853b..020cef6012e9 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -34,11 +34,11 @@ inline uint32_t packLocale(const char* language, const char* region) {
}
inline uint32_t dropRegion(uint32_t packed_locale) {
- return packed_locale & 0xFFFF0000lu;
+ return packed_locale & 0xFFFF0000LU;
}
inline bool hasRegion(uint32_t packed_locale) {
- return (packed_locale & 0x0000FFFFlu) != 0;
+ return (packed_locale & 0x0000FFFFLU) != 0;
}
const size_t SCRIPT_LENGTH = 4;
@@ -122,9 +122,9 @@ inline bool isRepresentative(uint32_t language_and_region, const char* script) {
return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
}
-const uint32_t US_SPANISH = 0x65735553lu; // es-US
-const uint32_t MEXICAN_SPANISH = 0x65734D58lu; // es-MX
-const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424lu; // es-419
+const uint32_t US_SPANISH = 0x65735553LU; // es-US
+const uint32_t MEXICAN_SPANISH = 0x65734D58LU; // es-MX
+const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424LU; // es-419
// The two locales es-US and es-MX are treated as special fallbacks for es-419.
// If there is no es-419, they are considered its equivalent.
@@ -225,8 +225,8 @@ void localeDataComputeScript(char out[4], const char* language, const char* regi
}
const uint32_t ENGLISH_STOP_LIST[2] = {
- 0x656E0000lu, // en
- 0x656E8400lu, // en-001
+ 0x656E0000LU, // en
+ 0x656E8400LU, // en-001
};
const char ENGLISH_CHARS[2] = {'e', 'n'};
const char LATIN_CHARS[4] = {'L', 'a', 't', 'n'};
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 7c381efec7ec..c276a238e2e1 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1446,733 +1446,733 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
- 0x616145544C61746Ellu, // aa_Latn_ET
- 0x616247454379726Cllu, // ab_Cyrl_GE
- 0xC42047484C61746Ellu, // abr_Latn_GH
- 0x904049444C61746Ellu, // ace_Latn_ID
- 0x9C4055474C61746Ellu, // ach_Latn_UG
- 0x806047484C61746Ellu, // ada_Latn_GH
- 0xE06052554379726Cllu, // ady_Cyrl_RU
- 0x6165495241767374llu, // ae_Avst_IR
- 0x8480544E41726162llu, // aeb_Arab_TN
- 0x61665A414C61746Ellu, // af_Latn_ZA
- 0xC0C0434D4C61746Ellu, // agq_Latn_CM
- 0xB8E0494E41686F6Dllu, // aho_Ahom_IN
- 0x616B47484C61746Ellu, // ak_Latn_GH
- 0xA940495158737578llu, // akk_Xsux_IQ
- 0xB560584B4C61746Ellu, // aln_Latn_XK
- 0xCD6052554379726Cllu, // alt_Cyrl_RU
- 0x616D455445746869llu, // am_Ethi_ET
- 0xB9804E474C61746Ellu, // amo_Latn_NG
- 0xE5C049444C61746Ellu, // aoz_Latn_ID
- 0x8DE0544741726162llu, // apd_Arab_TG
- 0x6172454741726162llu, // ar_Arab_EG
- 0x8A20495241726D69llu, // arc_Armi_IR
- 0x8A204A4F4E626174llu, // arc_Nbat_JO
- 0x8A20535950616C6Dllu, // arc_Palm_SY
- 0xB620434C4C61746Ellu, // arn_Latn_CL
- 0xBA20424F4C61746Ellu, // aro_Latn_BO
- 0xC220445A41726162llu, // arq_Arab_DZ
- 0xE2204D4141726162llu, // ary_Arab_MA
- 0xE620454741726162llu, // arz_Arab_EG
- 0x6173494E42656E67llu, // as_Beng_IN
- 0x8240545A4C61746Ellu, // asa_Latn_TZ
- 0x9240555353676E77llu, // ase_Sgnw_US
- 0xCE4045534C61746Ellu, // ast_Latn_ES
- 0xA66043414C61746Ellu, // atj_Latn_CA
- 0x617652554379726Cllu, // av_Cyrl_RU
- 0x82C0494E44657661llu, // awa_Deva_IN
- 0x6179424F4C61746Ellu, // ay_Latn_BO
- 0x617A495241726162llu, // az_Arab_IR
- 0x617A415A4C61746Ellu, // az_Latn_AZ
- 0x626152554379726Cllu, // ba_Cyrl_RU
- 0xAC01504B41726162llu, // bal_Arab_PK
- 0xB40149444C61746Ellu, // ban_Latn_ID
- 0xBC014E5044657661llu, // bap_Deva_NP
- 0xC40141544C61746Ellu, // bar_Latn_AT
- 0xC801434D4C61746Ellu, // bas_Latn_CM
- 0xDC01434D42616D75llu, // bax_Bamu_CM
- 0x882149444C61746Ellu, // bbc_Latn_ID
- 0xA421434D4C61746Ellu, // bbj_Latn_CM
- 0xA04143494C61746Ellu, // bci_Latn_CI
- 0x626542594379726Cllu, // be_Cyrl_BY
- 0xA481534441726162llu, // bej_Arab_SD
- 0xB0815A4D4C61746Ellu, // bem_Latn_ZM
- 0xD88149444C61746Ellu, // bew_Latn_ID
- 0xE481545A4C61746Ellu, // bez_Latn_TZ
- 0x8CA1434D4C61746Ellu, // bfd_Latn_CM
- 0xC0A1494E54616D6Cllu, // bfq_Taml_IN
- 0xCCA1504B41726162llu, // bft_Arab_PK
- 0xE0A1494E44657661llu, // bfy_Deva_IN
- 0x626742474379726Cllu, // bg_Cyrl_BG
- 0x88C1494E44657661llu, // bgc_Deva_IN
- 0xB4C1504B41726162llu, // bgn_Arab_PK
- 0xDCC154524772656Bllu, // bgx_Grek_TR
- 0x84E1494E44657661llu, // bhb_Deva_IN
- 0xA0E1494E44657661llu, // bhi_Deva_IN
- 0xA8E150484C61746Ellu, // bhk_Latn_PH
- 0xB8E1494E44657661llu, // bho_Deva_IN
- 0x626956554C61746Ellu, // bi_Latn_VU
- 0xA90150484C61746Ellu, // bik_Latn_PH
- 0xB5014E474C61746Ellu, // bin_Latn_NG
- 0xA521494E44657661llu, // bjj_Deva_IN
- 0xB52149444C61746Ellu, // bjn_Latn_ID
- 0xB141434D4C61746Ellu, // bkm_Latn_CM
- 0xD14150484C61746Ellu, // bku_Latn_PH
- 0xCD61564E54617674llu, // blt_Tavt_VN
- 0x626D4D4C4C61746Ellu, // bm_Latn_ML
- 0xC1814D4C4C61746Ellu, // bmq_Latn_ML
- 0x626E424442656E67llu, // bn_Beng_BD
- 0x626F434E54696274llu, // bo_Tibt_CN
- 0xE1E1494E42656E67llu, // bpy_Beng_IN
- 0xA201495241726162llu, // bqi_Arab_IR
- 0xD60143494C61746Ellu, // bqv_Latn_CI
- 0x627246524C61746Ellu, // br_Latn_FR
- 0x8221494E44657661llu, // bra_Deva_IN
- 0x9E21504B41726162llu, // brh_Arab_PK
- 0xDE21494E44657661llu, // brx_Deva_IN
- 0x627342414C61746Ellu, // bs_Latn_BA
- 0xC2414C5242617373llu, // bsq_Bass_LR
- 0xCA41434D4C61746Ellu, // bss_Latn_CM
- 0xBA6150484C61746Ellu, // bto_Latn_PH
- 0xD661504B44657661llu, // btv_Deva_PK
- 0x828152554379726Cllu, // bua_Cyrl_RU
- 0x8A8159544C61746Ellu, // buc_Latn_YT
- 0x9A8149444C61746Ellu, // bug_Latn_ID
- 0xB281434D4C61746Ellu, // bum_Latn_CM
- 0x86A147514C61746Ellu, // bvb_Latn_GQ
- 0xB701455245746869llu, // byn_Ethi_ER
- 0xD701434D4C61746Ellu, // byv_Latn_CM
- 0x93214D4C4C61746Ellu, // bze_Latn_ML
- 0x636145534C61746Ellu, // ca_Latn_ES
- 0x9C424E474C61746Ellu, // cch_Latn_NG
- 0xBC42494E42656E67llu, // ccp_Beng_IN
- 0xBC42424443616B6Dllu, // ccp_Cakm_BD
- 0x636552554379726Cllu, // ce_Cyrl_RU
- 0x848250484C61746Ellu, // ceb_Latn_PH
- 0x98C255474C61746Ellu, // cgg_Latn_UG
- 0x636847554C61746Ellu, // ch_Latn_GU
- 0xA8E2464D4C61746Ellu, // chk_Latn_FM
- 0xB0E252554379726Cllu, // chm_Cyrl_RU
- 0xB8E255534C61746Ellu, // cho_Latn_US
- 0xBCE243414C61746Ellu, // chp_Latn_CA
- 0xC4E2555343686572llu, // chr_Cher_US
- 0x81224B4841726162llu, // cja_Arab_KH
- 0xB122564E4368616Dllu, // cjm_Cham_VN
- 0x8542495141726162llu, // ckb_Arab_IQ
- 0x636F46524C61746Ellu, // co_Latn_FR
- 0xBDC24547436F7074llu, // cop_Copt_EG
- 0xC9E250484C61746Ellu, // cps_Latn_PH
- 0x6372434143616E73llu, // cr_Cans_CA
- 0xA622434143616E73llu, // crj_Cans_CA
- 0xAA22434143616E73llu, // crk_Cans_CA
- 0xAE22434143616E73llu, // crl_Cans_CA
- 0xB222434143616E73llu, // crm_Cans_CA
- 0xCA2253434C61746Ellu, // crs_Latn_SC
- 0x6373435A4C61746Ellu, // cs_Latn_CZ
- 0x8642504C4C61746Ellu, // csb_Latn_PL
- 0xDA42434143616E73llu, // csw_Cans_CA
- 0x8E624D4D50617563llu, // ctd_Pauc_MM
- 0x637552554379726Cllu, // cu_Cyrl_RU
- 0x63754247476C6167llu, // cu_Glag_BG
- 0x637652554379726Cllu, // cv_Cyrl_RU
- 0x637947424C61746Ellu, // cy_Latn_GB
- 0x6461444B4C61746Ellu, // da_Latn_DK
- 0xA80355534C61746Ellu, // dak_Latn_US
- 0xC40352554379726Cllu, // dar_Cyrl_RU
- 0xD4034B454C61746Ellu, // dav_Latn_KE
- 0x8843494E41726162llu, // dcc_Arab_IN
- 0x646544454C61746Ellu, // de_Latn_DE
- 0xB48343414C61746Ellu, // den_Latn_CA
- 0xC4C343414C61746Ellu, // dgr_Latn_CA
- 0x91234E454C61746Ellu, // dje_Latn_NE
- 0xA5A343494C61746Ellu, // dnj_Latn_CI
- 0xA1C3494E41726162llu, // doi_Arab_IN
- 0x864344454C61746Ellu, // dsb_Latn_DE
- 0xB2634D4C4C61746Ellu, // dtm_Latn_ML
- 0xBE634D594C61746Ellu, // dtp_Latn_MY
- 0xE2634E5044657661llu, // dty_Deva_NP
- 0x8283434D4C61746Ellu, // dua_Latn_CM
- 0x64764D5654686161llu, // dv_Thaa_MV
- 0xBB03534E4C61746Ellu, // dyo_Latn_SN
- 0xD30342464C61746Ellu, // dyu_Latn_BF
- 0x647A425454696274llu, // dz_Tibt_BT
- 0xD0244B454C61746Ellu, // ebu_Latn_KE
- 0x656547484C61746Ellu, // ee_Latn_GH
- 0xA0A44E474C61746Ellu, // efi_Latn_NG
- 0xACC449544C61746Ellu, // egl_Latn_IT
- 0xE0C4454745677970llu, // egy_Egyp_EG
- 0xE1444D4D4B616C69llu, // eky_Kali_MM
- 0x656C47524772656Bllu, // el_Grek_GR
- 0x656E47424C61746Ellu, // en_Latn_GB
- 0x656E55534C61746Ellu, // en_Latn_US
- 0x656E474253686177llu, // en_Shaw_GB
- 0x657345534C61746Ellu, // es_Latn_ES
- 0x65734D584C61746Ellu, // es_Latn_MX
- 0x657355534C61746Ellu, // es_Latn_US
- 0xD24455534C61746Ellu, // esu_Latn_US
- 0x657445454C61746Ellu, // et_Latn_EE
- 0xCE6449544974616Cllu, // ett_Ital_IT
- 0x657545534C61746Ellu, // eu_Latn_ES
- 0xBAC4434D4C61746Ellu, // ewo_Latn_CM
- 0xCEE445534C61746Ellu, // ext_Latn_ES
- 0x6661495241726162llu, // fa_Arab_IR
- 0xB40547514C61746Ellu, // fan_Latn_GQ
- 0x6666474E41646C6Dllu, // ff_Adlm_GN
- 0x6666534E4C61746Ellu, // ff_Latn_SN
- 0xB0A54D4C4C61746Ellu, // ffm_Latn_ML
- 0x666946494C61746Ellu, // fi_Latn_FI
- 0x8105534441726162llu, // fia_Arab_SD
- 0xAD0550484C61746Ellu, // fil_Latn_PH
- 0xCD0553454C61746Ellu, // fit_Latn_SE
- 0x666A464A4C61746Ellu, // fj_Latn_FJ
- 0x666F464F4C61746Ellu, // fo_Latn_FO
- 0xB5C5424A4C61746Ellu, // fon_Latn_BJ
- 0x667246524C61746Ellu, // fr_Latn_FR
- 0x8A2555534C61746Ellu, // frc_Latn_US
- 0xBE2546524C61746Ellu, // frp_Latn_FR
- 0xC62544454C61746Ellu, // frr_Latn_DE
- 0xCA2544454C61746Ellu, // frs_Latn_DE
- 0x8685434D41726162llu, // fub_Arab_CM
- 0x8E8557464C61746Ellu, // fud_Latn_WF
- 0x9685474E4C61746Ellu, // fuf_Latn_GN
- 0xC2854E454C61746Ellu, // fuq_Latn_NE
- 0xC68549544C61746Ellu, // fur_Latn_IT
- 0xD6854E474C61746Ellu, // fuv_Latn_NG
- 0xC6A553444C61746Ellu, // fvr_Latn_SD
- 0x66794E4C4C61746Ellu, // fy_Latn_NL
- 0x676149454C61746Ellu, // ga_Latn_IE
- 0x800647484C61746Ellu, // gaa_Latn_GH
- 0x98064D444C61746Ellu, // gag_Latn_MD
- 0xB406434E48616E73llu, // gan_Hans_CN
- 0xE00649444C61746Ellu, // gay_Latn_ID
- 0xB026494E44657661llu, // gbm_Deva_IN
- 0xE426495241726162llu, // gbz_Arab_IR
- 0xC44647464C61746Ellu, // gcr_Latn_GF
- 0x676447424C61746Ellu, // gd_Latn_GB
- 0xE486455445746869llu, // gez_Ethi_ET
- 0xB4C64E5044657661llu, // ggn_Deva_NP
- 0xAD064B494C61746Ellu, // gil_Latn_KI
- 0xA926504B41726162llu, // gjk_Arab_PK
- 0xD126504B41726162llu, // gju_Arab_PK
- 0x676C45534C61746Ellu, // gl_Latn_ES
- 0xA966495241726162llu, // glk_Arab_IR
- 0x676E50594C61746Ellu, // gn_Latn_PY
- 0xB1C6494E44657661llu, // gom_Deva_IN
- 0xB5C6494E54656C75llu, // gon_Telu_IN
- 0xC5C649444C61746Ellu, // gor_Latn_ID
- 0xC9C64E4C4C61746Ellu, // gos_Latn_NL
- 0xCDC65541476F7468llu, // got_Goth_UA
- 0x8A26435943707274llu, // grc_Cprt_CY
- 0x8A2647524C696E62llu, // grc_Linb_GR
- 0xCE26494E42656E67llu, // grt_Beng_IN
- 0xDA4643484C61746Ellu, // gsw_Latn_CH
- 0x6775494E47756A72llu, // gu_Gujr_IN
- 0x868642524C61746Ellu, // gub_Latn_BR
- 0x8A86434F4C61746Ellu, // guc_Latn_CO
- 0xC68647484C61746Ellu, // gur_Latn_GH
- 0xE6864B454C61746Ellu, // guz_Latn_KE
- 0x6776494D4C61746Ellu, // gv_Latn_IM
- 0xC6A64E5044657661llu, // gvr_Deva_NP
- 0xA2C643414C61746Ellu, // gwi_Latn_CA
- 0x68614E474C61746Ellu, // ha_Latn_NG
- 0xA807434E48616E73llu, // hak_Hans_CN
- 0xD80755534C61746Ellu, // haw_Latn_US
- 0xE407414641726162llu, // haz_Arab_AF
- 0x6865494C48656272llu, // he_Hebr_IL
- 0x6869494E44657661llu, // hi_Deva_IN
- 0x9507464A4C61746Ellu, // hif_Latn_FJ
- 0xAD0750484C61746Ellu, // hil_Latn_PH
- 0xD1675452486C7577llu, // hlu_Hluw_TR
- 0x8D87434E506C7264llu, // hmd_Plrd_CN
- 0x8DA7504B41726162llu, // hnd_Arab_PK
- 0x91A7494E44657661llu, // hne_Deva_IN
- 0xA5A74C41486D6E67llu, // hnj_Hmng_LA
- 0xB5A750484C61746Ellu, // hnn_Latn_PH
- 0xB9A7504B41726162llu, // hno_Arab_PK
- 0x686F50474C61746Ellu, // ho_Latn_PG
- 0x89C7494E44657661llu, // hoc_Deva_IN
- 0xA5C7494E44657661llu, // hoj_Deva_IN
- 0x687248524C61746Ellu, // hr_Latn_HR
- 0x864744454C61746Ellu, // hsb_Latn_DE
- 0xB647434E48616E73llu, // hsn_Hans_CN
- 0x687448544C61746Ellu, // ht_Latn_HT
- 0x687548554C61746Ellu, // hu_Latn_HU
- 0x6879414D41726D6Ellu, // hy_Armn_AM
- 0x687A4E414C61746Ellu, // hz_Latn_NA
- 0x696146524C61746Ellu, // ia_Latn_FR
- 0x80284D594C61746Ellu, // iba_Latn_MY
- 0x84284E474C61746Ellu, // ibb_Latn_NG
- 0x696449444C61746Ellu, // id_Latn_ID
- 0x69674E474C61746Ellu, // ig_Latn_NG
- 0x6969434E59696969llu, // ii_Yiii_CN
- 0x696B55534C61746Ellu, // ik_Latn_US
- 0xCD4843414C61746Ellu, // ikt_Latn_CA
- 0xB96850484C61746Ellu, // ilo_Latn_PH
- 0x696E49444C61746Ellu, // in_Latn_ID
- 0x9DA852554379726Cllu, // inh_Cyrl_RU
- 0x697349534C61746Ellu, // is_Latn_IS
- 0x697449544C61746Ellu, // it_Latn_IT
- 0x6975434143616E73llu, // iu_Cans_CA
- 0x6977494C48656272llu, // iw_Hebr_IL
- 0x9F2852554C61746Ellu, // izh_Latn_RU
- 0x6A614A504A70616Ellu, // ja_Jpan_JP
- 0xB0094A4D4C61746Ellu, // jam_Latn_JM
- 0xB8C9434D4C61746Ellu, // jgo_Latn_CM
- 0x8989545A4C61746Ellu, // jmc_Latn_TZ
- 0xAD894E5044657661llu, // jml_Deva_NP
- 0xCE89444B4C61746Ellu, // jut_Latn_DK
- 0x6A7649444C61746Ellu, // jv_Latn_ID
- 0x6A7749444C61746Ellu, // jw_Latn_ID
- 0x6B61474547656F72llu, // ka_Geor_GE
- 0x800A555A4379726Cllu, // kaa_Cyrl_UZ
- 0x840A445A4C61746Ellu, // kab_Latn_DZ
- 0x880A4D4D4C61746Ellu, // kac_Latn_MM
- 0xA40A4E474C61746Ellu, // kaj_Latn_NG
- 0xB00A4B454C61746Ellu, // kam_Latn_KE
- 0xB80A4D4C4C61746Ellu, // kao_Latn_ML
- 0x8C2A52554379726Cllu, // kbd_Cyrl_RU
- 0xE02A4E4541726162llu, // kby_Arab_NE
- 0x984A4E474C61746Ellu, // kcg_Latn_NG
- 0xA84A5A574C61746Ellu, // kck_Latn_ZW
- 0x906A545A4C61746Ellu, // kde_Latn_TZ
- 0x9C6A544741726162llu, // kdh_Arab_TG
- 0xCC6A544854686169llu, // kdt_Thai_TH
- 0x808A43564C61746Ellu, // kea_Latn_CV
- 0xB48A434D4C61746Ellu, // ken_Latn_CM
- 0xB8AA43494C61746Ellu, // kfo_Latn_CI
- 0xC4AA494E44657661llu, // kfr_Deva_IN
- 0xE0AA494E44657661llu, // kfy_Deva_IN
- 0x6B6743444C61746Ellu, // kg_Latn_CD
- 0x90CA49444C61746Ellu, // kge_Latn_ID
- 0xBCCA42524C61746Ellu, // kgp_Latn_BR
- 0x80EA494E4C61746Ellu, // kha_Latn_IN
- 0x84EA434E54616C75llu, // khb_Talu_CN
- 0xB4EA494E44657661llu, // khn_Deva_IN
- 0xC0EA4D4C4C61746Ellu, // khq_Latn_ML
- 0xCCEA494E4D796D72llu, // kht_Mymr_IN
- 0xD8EA504B41726162llu, // khw_Arab_PK
- 0x6B694B454C61746Ellu, // ki_Latn_KE
- 0xD10A54524C61746Ellu, // kiu_Latn_TR
- 0x6B6A4E414C61746Ellu, // kj_Latn_NA
- 0x992A4C414C616F6Fllu, // kjg_Laoo_LA
- 0x6B6B434E41726162llu, // kk_Arab_CN
- 0x6B6B4B5A4379726Cllu, // kk_Cyrl_KZ
- 0xA54A434D4C61746Ellu, // kkj_Latn_CM
- 0x6B6C474C4C61746Ellu, // kl_Latn_GL
- 0xB56A4B454C61746Ellu, // kln_Latn_KE
- 0x6B6D4B484B686D72llu, // km_Khmr_KH
- 0x858A414F4C61746Ellu, // kmb_Latn_AO
- 0x6B6E494E4B6E6461llu, // kn_Knda_IN
- 0x6B6F4B524B6F7265llu, // ko_Kore_KR
- 0xA1CA52554379726Cllu, // koi_Cyrl_RU
- 0xA9CA494E44657661llu, // kok_Deva_IN
- 0xC9CA464D4C61746Ellu, // kos_Latn_FM
- 0x91EA4C524C61746Ellu, // kpe_Latn_LR
- 0x8A2A52554379726Cllu, // krc_Cyrl_RU
- 0xA22A534C4C61746Ellu, // kri_Latn_SL
- 0xA62A50484C61746Ellu, // krj_Latn_PH
- 0xAE2A52554C61746Ellu, // krl_Latn_RU
- 0xD22A494E44657661llu, // kru_Deva_IN
- 0x6B73494E41726162llu, // ks_Arab_IN
- 0x864A545A4C61746Ellu, // ksb_Latn_TZ
- 0x964A434D4C61746Ellu, // ksf_Latn_CM
- 0x9E4A44454C61746Ellu, // ksh_Latn_DE
- 0x6B75495141726162llu, // ku_Arab_IQ
- 0x6B7554524C61746Ellu, // ku_Latn_TR
- 0xB28A52554379726Cllu, // kum_Cyrl_RU
- 0x6B7652554379726Cllu, // kv_Cyrl_RU
- 0xC6AA49444C61746Ellu, // kvr_Latn_ID
- 0xDEAA504B41726162llu, // kvx_Arab_PK
- 0x6B7747424C61746Ellu, // kw_Latn_GB
- 0xB2EA544854686169llu, // kxm_Thai_TH
- 0xBEEA504B41726162llu, // kxp_Arab_PK
- 0x6B79434E41726162llu, // ky_Arab_CN
- 0x6B794B474379726Cllu, // ky_Cyrl_KG
- 0x6B7954524C61746Ellu, // ky_Latn_TR
- 0x6C6156414C61746Ellu, // la_Latn_VA
- 0x840B47524C696E61llu, // lab_Lina_GR
- 0x8C0B494C48656272llu, // lad_Hebr_IL
- 0x980B545A4C61746Ellu, // lag_Latn_TZ
- 0x9C0B504B41726162llu, // lah_Arab_PK
- 0xA40B55474C61746Ellu, // laj_Latn_UG
- 0x6C624C554C61746Ellu, // lb_Latn_LU
- 0x902B52554379726Cllu, // lbe_Cyrl_RU
- 0xD82B49444C61746Ellu, // lbw_Latn_ID
- 0xBC4B434E54686169llu, // lcp_Thai_CN
- 0xBC8B494E4C657063llu, // lep_Lepc_IN
- 0xE48B52554379726Cllu, // lez_Cyrl_RU
- 0x6C6755474C61746Ellu, // lg_Latn_UG
- 0x6C694E4C4C61746Ellu, // li_Latn_NL
- 0x950B4E5044657661llu, // lif_Deva_NP
- 0x950B494E4C696D62llu, // lif_Limb_IN
- 0xA50B49544C61746Ellu, // lij_Latn_IT
- 0xC90B434E4C697375llu, // lis_Lisu_CN
- 0xBD2B49444C61746Ellu, // ljp_Latn_ID
- 0xA14B495241726162llu, // lki_Arab_IR
- 0xCD4B55534C61746Ellu, // lkt_Latn_US
- 0xB58B494E54656C75llu, // lmn_Telu_IN
- 0xB98B49544C61746Ellu, // lmo_Latn_IT
- 0x6C6E43444C61746Ellu, // ln_Latn_CD
- 0x6C6F4C414C616F6Fllu, // lo_Laoo_LA
- 0xADCB43444C61746Ellu, // lol_Latn_CD
- 0xE5CB5A4D4C61746Ellu, // loz_Latn_ZM
- 0x8A2B495241726162llu, // lrc_Arab_IR
- 0x6C744C544C61746Ellu, // lt_Latn_LT
- 0x9A6B4C564C61746Ellu, // ltg_Latn_LV
- 0x6C7543444C61746Ellu, // lu_Latn_CD
- 0x828B43444C61746Ellu, // lua_Latn_CD
- 0xBA8B4B454C61746Ellu, // luo_Latn_KE
- 0xE28B4B454C61746Ellu, // luy_Latn_KE
- 0xE68B495241726162llu, // luz_Arab_IR
- 0x6C764C564C61746Ellu, // lv_Latn_LV
- 0xAECB544854686169llu, // lwl_Thai_TH
- 0x9F2B434E48616E73llu, // lzh_Hans_CN
- 0xE72B54524C61746Ellu, // lzz_Latn_TR
- 0x8C0C49444C61746Ellu, // mad_Latn_ID
- 0x940C434D4C61746Ellu, // maf_Latn_CM
- 0x980C494E44657661llu, // mag_Deva_IN
- 0xA00C494E44657661llu, // mai_Deva_IN
- 0xA80C49444C61746Ellu, // mak_Latn_ID
- 0xB40C474D4C61746Ellu, // man_Latn_GM
- 0xB40C474E4E6B6F6Fllu, // man_Nkoo_GN
- 0xC80C4B454C61746Ellu, // mas_Latn_KE
- 0xE40C4D584C61746Ellu, // maz_Latn_MX
- 0x946C52554379726Cllu, // mdf_Cyrl_RU
- 0x9C6C50484C61746Ellu, // mdh_Latn_PH
- 0xC46C49444C61746Ellu, // mdr_Latn_ID
- 0xB48C534C4C61746Ellu, // men_Latn_SL
- 0xC48C4B454C61746Ellu, // mer_Latn_KE
- 0x80AC544841726162llu, // mfa_Arab_TH
- 0x90AC4D554C61746Ellu, // mfe_Latn_MU
- 0x6D674D474C61746Ellu, // mg_Latn_MG
- 0x9CCC4D5A4C61746Ellu, // mgh_Latn_MZ
- 0xB8CC434D4C61746Ellu, // mgo_Latn_CM
- 0xBCCC4E5044657661llu, // mgp_Deva_NP
- 0xE0CC545A4C61746Ellu, // mgy_Latn_TZ
- 0x6D684D484C61746Ellu, // mh_Latn_MH
- 0x6D694E5A4C61746Ellu, // mi_Latn_NZ
- 0xB50C49444C61746Ellu, // min_Latn_ID
- 0xC90C495148617472llu, // mis_Hatr_IQ
- 0x6D6B4D4B4379726Cllu, // mk_Cyrl_MK
- 0x6D6C494E4D6C796Dllu, // ml_Mlym_IN
- 0xC96C53444C61746Ellu, // mls_Latn_SD
- 0x6D6E4D4E4379726Cllu, // mn_Cyrl_MN
- 0x6D6E434E4D6F6E67llu, // mn_Mong_CN
- 0xA1AC494E42656E67llu, // mni_Beng_IN
- 0xD9AC4D4D4D796D72llu, // mnw_Mymr_MM
- 0x91CC43414C61746Ellu, // moe_Latn_CA
- 0x9DCC43414C61746Ellu, // moh_Latn_CA
- 0xC9CC42464C61746Ellu, // mos_Latn_BF
- 0x6D72494E44657661llu, // mr_Deva_IN
- 0x8E2C4E5044657661llu, // mrd_Deva_NP
- 0xA62C52554379726Cllu, // mrj_Cyrl_RU
- 0xBA2C42444D726F6Fllu, // mro_Mroo_BD
- 0x6D734D594C61746Ellu, // ms_Latn_MY
- 0x6D744D544C61746Ellu, // mt_Latn_MT
- 0xC66C494E44657661llu, // mtr_Deva_IN
- 0x828C434D4C61746Ellu, // mua_Latn_CM
- 0xCA8C55534C61746Ellu, // mus_Latn_US
- 0xE2AC504B41726162llu, // mvy_Arab_PK
- 0xAACC4D4C4C61746Ellu, // mwk_Latn_ML
- 0xC6CC494E44657661llu, // mwr_Deva_IN
- 0xD6CC49444C61746Ellu, // mwv_Latn_ID
- 0x8AEC5A574C61746Ellu, // mxc_Latn_ZW
- 0x6D794D4D4D796D72llu, // my_Mymr_MM
- 0xD70C52554379726Cllu, // myv_Cyrl_RU
- 0xDF0C55474C61746Ellu, // myx_Latn_UG
- 0xE70C49524D616E64llu, // myz_Mand_IR
- 0xB72C495241726162llu, // mzn_Arab_IR
- 0x6E614E524C61746Ellu, // na_Latn_NR
- 0xB40D434E48616E73llu, // nan_Hans_CN
- 0xBC0D49544C61746Ellu, // nap_Latn_IT
- 0xC00D4E414C61746Ellu, // naq_Latn_NA
- 0x6E624E4F4C61746Ellu, // nb_Latn_NO
- 0x9C4D4D584C61746Ellu, // nch_Latn_MX
- 0x6E645A574C61746Ellu, // nd_Latn_ZW
- 0x886D4D5A4C61746Ellu, // ndc_Latn_MZ
- 0xC86D44454C61746Ellu, // nds_Latn_DE
- 0x6E654E5044657661llu, // ne_Deva_NP
- 0xD88D4E5044657661llu, // new_Deva_NP
- 0x6E674E414C61746Ellu, // ng_Latn_NA
- 0xACCD4D5A4C61746Ellu, // ngl_Latn_MZ
- 0x90ED4D584C61746Ellu, // nhe_Latn_MX
- 0xD8ED4D584C61746Ellu, // nhw_Latn_MX
- 0xA50D49444C61746Ellu, // nij_Latn_ID
- 0xD10D4E554C61746Ellu, // niu_Latn_NU
- 0xB92D494E4C61746Ellu, // njo_Latn_IN
- 0x6E6C4E4C4C61746Ellu, // nl_Latn_NL
- 0x998D434D4C61746Ellu, // nmg_Latn_CM
- 0x6E6E4E4F4C61746Ellu, // nn_Latn_NO
- 0x9DAD434D4C61746Ellu, // nnh_Latn_CM
- 0x6E6F4E4F4C61746Ellu, // no_Latn_NO
- 0x8DCD54484C616E61llu, // nod_Lana_TH
- 0x91CD494E44657661llu, // noe_Deva_IN
- 0xB5CD534552756E72llu, // non_Runr_SE
- 0xBA0D474E4E6B6F6Fllu, // nqo_Nkoo_GN
- 0x6E725A414C61746Ellu, // nr_Latn_ZA
- 0xAA4D434143616E73llu, // nsk_Cans_CA
- 0xBA4D5A414C61746Ellu, // nso_Latn_ZA
- 0xCA8D53534C61746Ellu, // nus_Latn_SS
- 0x6E7655534C61746Ellu, // nv_Latn_US
- 0xC2ED434E4C61746Ellu, // nxq_Latn_CN
- 0x6E794D574C61746Ellu, // ny_Latn_MW
- 0xB30D545A4C61746Ellu, // nym_Latn_TZ
- 0xB70D55474C61746Ellu, // nyn_Latn_UG
- 0xA32D47484C61746Ellu, // nzi_Latn_GH
- 0x6F6346524C61746Ellu, // oc_Latn_FR
- 0x6F6D45544C61746Ellu, // om_Latn_ET
- 0x6F72494E4F727961llu, // or_Orya_IN
- 0x6F7347454379726Cllu, // os_Cyrl_GE
- 0x824E55534F736765llu, // osa_Osge_US
- 0xAA6E4D4E4F726B68llu, // otk_Orkh_MN
- 0x7061504B41726162llu, // pa_Arab_PK
- 0x7061494E47757275llu, // pa_Guru_IN
- 0x980F50484C61746Ellu, // pag_Latn_PH
- 0xAC0F495250686C69llu, // pal_Phli_IR
- 0xAC0F434E50686C70llu, // pal_Phlp_CN
- 0xB00F50484C61746Ellu, // pam_Latn_PH
- 0xBC0F41574C61746Ellu, // pap_Latn_AW
- 0xD00F50574C61746Ellu, // pau_Latn_PW
- 0x8C4F46524C61746Ellu, // pcd_Latn_FR
- 0xB04F4E474C61746Ellu, // pcm_Latn_NG
- 0x886F55534C61746Ellu, // pdc_Latn_US
- 0xCC6F43414C61746Ellu, // pdt_Latn_CA
- 0xB88F49525870656Fllu, // peo_Xpeo_IR
- 0xACAF44454C61746Ellu, // pfl_Latn_DE
- 0xB4EF4C4250686E78llu, // phn_Phnx_LB
- 0x814F494E42726168llu, // pka_Brah_IN
- 0xB94F4B454C61746Ellu, // pko_Latn_KE
- 0x706C504C4C61746Ellu, // pl_Latn_PL
- 0xC98F49544C61746Ellu, // pms_Latn_IT
- 0xCDAF47524772656Bllu, // pnt_Grek_GR
- 0xB5CF464D4C61746Ellu, // pon_Latn_FM
- 0x822F504B4B686172llu, // pra_Khar_PK
- 0x8E2F495241726162llu, // prd_Arab_IR
- 0x7073414641726162llu, // ps_Arab_AF
- 0x707442524C61746Ellu, // pt_Latn_BR
- 0xD28F47414C61746Ellu, // puu_Latn_GA
- 0x717550454C61746Ellu, // qu_Latn_PE
- 0x8A9047544C61746Ellu, // quc_Latn_GT
- 0x9A9045434C61746Ellu, // qug_Latn_EC
- 0xA411494E44657661llu, // raj_Deva_IN
- 0x945152454C61746Ellu, // rcf_Latn_RE
- 0xA49149444C61746Ellu, // rej_Latn_ID
- 0xB4D149544C61746Ellu, // rgn_Latn_IT
- 0x8111494E4C61746Ellu, // ria_Latn_IN
- 0x95114D4154666E67llu, // rif_Tfng_MA
- 0xC9314E5044657661llu, // rjs_Deva_NP
- 0xCD51424442656E67llu, // rkt_Beng_BD
- 0x726D43484C61746Ellu, // rm_Latn_CH
- 0x959146494C61746Ellu, // rmf_Latn_FI
- 0xB99143484C61746Ellu, // rmo_Latn_CH
- 0xCD91495241726162llu, // rmt_Arab_IR
- 0xD19153454C61746Ellu, // rmu_Latn_SE
- 0x726E42494C61746Ellu, // rn_Latn_BI
- 0x99B14D5A4C61746Ellu, // rng_Latn_MZ
- 0x726F524F4C61746Ellu, // ro_Latn_RO
- 0x85D149444C61746Ellu, // rob_Latn_ID
- 0x95D1545A4C61746Ellu, // rof_Latn_TZ
- 0xB271464A4C61746Ellu, // rtm_Latn_FJ
- 0x727552554379726Cllu, // ru_Cyrl_RU
- 0x929155414379726Cllu, // rue_Cyrl_UA
- 0x9A9153424C61746Ellu, // rug_Latn_SB
- 0x727752574C61746Ellu, // rw_Latn_RW
- 0xAAD1545A4C61746Ellu, // rwk_Latn_TZ
- 0xD3114A504B616E61llu, // ryu_Kana_JP
- 0x7361494E44657661llu, // sa_Deva_IN
- 0x941247484C61746Ellu, // saf_Latn_GH
- 0x9C1252554379726Cllu, // sah_Cyrl_RU
- 0xC0124B454C61746Ellu, // saq_Latn_KE
- 0xC81249444C61746Ellu, // sas_Latn_ID
- 0xCC12494E4C61746Ellu, // sat_Latn_IN
- 0xE412494E53617572llu, // saz_Saur_IN
- 0xBC32545A4C61746Ellu, // sbp_Latn_TZ
- 0x736349544C61746Ellu, // sc_Latn_IT
- 0xA852494E44657661llu, // sck_Deva_IN
- 0xB45249544C61746Ellu, // scn_Latn_IT
- 0xB85247424C61746Ellu, // sco_Latn_GB
- 0xC85243414C61746Ellu, // scs_Latn_CA
- 0x7364504B41726162llu, // sd_Arab_PK
- 0x7364494E44657661llu, // sd_Deva_IN
- 0x7364494E4B686F6Allu, // sd_Khoj_IN
- 0x7364494E53696E64llu, // sd_Sind_IN
- 0x887249544C61746Ellu, // sdc_Latn_IT
- 0x9C72495241726162llu, // sdh_Arab_IR
- 0x73654E4F4C61746Ellu, // se_Latn_NO
- 0x949243494C61746Ellu, // sef_Latn_CI
- 0x9C924D5A4C61746Ellu, // seh_Latn_MZ
- 0xA0924D584C61746Ellu, // sei_Latn_MX
- 0xC8924D4C4C61746Ellu, // ses_Latn_ML
- 0x736743464C61746Ellu, // sg_Latn_CF
- 0x80D249454F67616Dllu, // sga_Ogam_IE
- 0xC8D24C544C61746Ellu, // sgs_Latn_LT
- 0xA0F24D4154666E67llu, // shi_Tfng_MA
- 0xB4F24D4D4D796D72llu, // shn_Mymr_MM
- 0x73694C4B53696E68llu, // si_Sinh_LK
- 0x8D1245544C61746Ellu, // sid_Latn_ET
- 0x736B534B4C61746Ellu, // sk_Latn_SK
- 0xC552504B41726162llu, // skr_Arab_PK
- 0x736C53494C61746Ellu, // sl_Latn_SI
- 0xA172504C4C61746Ellu, // sli_Latn_PL
- 0xE17249444C61746Ellu, // sly_Latn_ID
- 0x736D57534C61746Ellu, // sm_Latn_WS
- 0x819253454C61746Ellu, // sma_Latn_SE
- 0xA59253454C61746Ellu, // smj_Latn_SE
- 0xB59246494C61746Ellu, // smn_Latn_FI
- 0xBD92494C53616D72llu, // smp_Samr_IL
- 0xC99246494C61746Ellu, // sms_Latn_FI
- 0x736E5A574C61746Ellu, // sn_Latn_ZW
- 0xA9B24D4C4C61746Ellu, // snk_Latn_ML
- 0x736F534F4C61746Ellu, // so_Latn_SO
- 0xD1D2544854686169llu, // sou_Thai_TH
- 0x7371414C4C61746Ellu, // sq_Latn_AL
- 0x737252534379726Cllu, // sr_Cyrl_RS
- 0x737252534C61746Ellu, // sr_Latn_RS
- 0x8632494E536F7261llu, // srb_Sora_IN
- 0xB63253524C61746Ellu, // srn_Latn_SR
- 0xC632534E4C61746Ellu, // srr_Latn_SN
- 0xDE32494E44657661llu, // srx_Deva_IN
- 0x73735A414C61746Ellu, // ss_Latn_ZA
- 0xE25245524C61746Ellu, // ssy_Latn_ER
- 0x73745A414C61746Ellu, // st_Latn_ZA
- 0xC27244454C61746Ellu, // stq_Latn_DE
- 0x737549444C61746Ellu, // su_Latn_ID
- 0xAA92545A4C61746Ellu, // suk_Latn_TZ
- 0xCA92474E4C61746Ellu, // sus_Latn_GN
- 0x737653454C61746Ellu, // sv_Latn_SE
- 0x7377545A4C61746Ellu, // sw_Latn_TZ
- 0x86D2595441726162llu, // swb_Arab_YT
- 0x8AD243444C61746Ellu, // swc_Latn_CD
- 0x9AD244454C61746Ellu, // swg_Latn_DE
- 0xD6D2494E44657661llu, // swv_Deva_IN
- 0xB6F249444C61746Ellu, // sxn_Latn_ID
- 0xAF12424442656E67llu, // syl_Beng_BD
- 0xC712495153797263llu, // syr_Syrc_IQ
- 0xAF32504C4C61746Ellu, // szl_Latn_PL
- 0x7461494E54616D6Cllu, // ta_Taml_IN
- 0xA4134E5044657661llu, // taj_Deva_NP
- 0xD83350484C61746Ellu, // tbw_Latn_PH
- 0xE053494E4B6E6461llu, // tcy_Knda_IN
- 0x8C73434E54616C65llu, // tdd_Tale_CN
- 0x98734E5044657661llu, // tdg_Deva_NP
- 0x9C734E5044657661llu, // tdh_Deva_NP
- 0x7465494E54656C75llu, // te_Telu_IN
- 0xB093534C4C61746Ellu, // tem_Latn_SL
- 0xB89355474C61746Ellu, // teo_Latn_UG
- 0xCC93544C4C61746Ellu, // tet_Latn_TL
- 0x7467504B41726162llu, // tg_Arab_PK
- 0x7467544A4379726Cllu, // tg_Cyrl_TJ
- 0x7468544854686169llu, // th_Thai_TH
- 0xACF34E5044657661llu, // thl_Deva_NP
- 0xC0F34E5044657661llu, // thq_Deva_NP
- 0xC4F34E5044657661llu, // thr_Deva_NP
- 0x7469455445746869llu, // ti_Ethi_ET
- 0x9913455245746869llu, // tig_Ethi_ER
- 0xD5134E474C61746Ellu, // tiv_Latn_NG
- 0x746B544D4C61746Ellu, // tk_Latn_TM
- 0xAD53544B4C61746Ellu, // tkl_Latn_TK
- 0xC553415A4C61746Ellu, // tkr_Latn_AZ
- 0xCD534E5044657661llu, // tkt_Deva_NP
- 0x746C50484C61746Ellu, // tl_Latn_PH
- 0xE173415A4C61746Ellu, // tly_Latn_AZ
- 0x9D934E454C61746Ellu, // tmh_Latn_NE
- 0x746E5A414C61746Ellu, // tn_Latn_ZA
- 0x746F544F4C61746Ellu, // to_Latn_TO
- 0x99D34D574C61746Ellu, // tog_Latn_MW
- 0xA1F350474C61746Ellu, // tpi_Latn_PG
- 0x747254524C61746Ellu, // tr_Latn_TR
- 0xD23354524C61746Ellu, // tru_Latn_TR
- 0xD63354574C61746Ellu, // trv_Latn_TW
- 0x74735A414C61746Ellu, // ts_Latn_ZA
- 0x8E5347524772656Bllu, // tsd_Grek_GR
- 0x96534E5044657661llu, // tsf_Deva_NP
- 0x9A5350484C61746Ellu, // tsg_Latn_PH
- 0xA653425454696274llu, // tsj_Tibt_BT
- 0x747452554379726Cllu, // tt_Cyrl_RU
- 0xA67355474C61746Ellu, // ttj_Latn_UG
- 0xCA73544854686169llu, // tts_Thai_TH
- 0xCE73415A4C61746Ellu, // ttt_Latn_AZ
- 0xB2934D574C61746Ellu, // tum_Latn_MW
- 0xAEB354564C61746Ellu, // tvl_Latn_TV
- 0xC2D34E454C61746Ellu, // twq_Latn_NE
- 0x9AF3434E54616E67llu, // txg_Tang_CN
- 0x747950464C61746Ellu, // ty_Latn_PF
- 0xD71352554379726Cllu, // tyv_Cyrl_RU
- 0xB3334D414C61746Ellu, // tzm_Latn_MA
- 0xB07452554379726Cllu, // udm_Cyrl_RU
- 0x7567434E41726162llu, // ug_Arab_CN
- 0x75674B5A4379726Cllu, // ug_Cyrl_KZ
- 0x80D4535955676172llu, // uga_Ugar_SY
- 0x756B55414379726Cllu, // uk_Cyrl_UA
- 0xA174464D4C61746Ellu, // uli_Latn_FM
- 0x8594414F4C61746Ellu, // umb_Latn_AO
- 0xC5B4494E42656E67llu, // unr_Beng_IN
- 0xC5B44E5044657661llu, // unr_Deva_NP
- 0xDDB4494E42656E67llu, // unx_Beng_IN
- 0x7572504B41726162llu, // ur_Arab_PK
- 0x757A414641726162llu, // uz_Arab_AF
- 0x757A555A4C61746Ellu, // uz_Latn_UZ
- 0xA0154C5256616969llu, // vai_Vaii_LR
- 0x76655A414C61746Ellu, // ve_Latn_ZA
- 0x889549544C61746Ellu, // vec_Latn_IT
- 0xBC9552554C61746Ellu, // vep_Latn_RU
- 0x7669564E4C61746Ellu, // vi_Latn_VN
- 0x891553584C61746Ellu, // vic_Latn_SX
- 0xC97542454C61746Ellu, // vls_Latn_BE
- 0x959544454C61746Ellu, // vmf_Latn_DE
- 0xD9954D5A4C61746Ellu, // vmw_Latn_MZ
- 0xCDD552554C61746Ellu, // vot_Latn_RU
- 0xBA3545454C61746Ellu, // vro_Latn_EE
- 0xB695545A4C61746Ellu, // vun_Latn_TZ
- 0x776142454C61746Ellu, // wa_Latn_BE
- 0x901643484C61746Ellu, // wae_Latn_CH
- 0xAC16455445746869llu, // wal_Ethi_ET
- 0xC41650484C61746Ellu, // war_Latn_PH
- 0xBC3641554C61746Ellu, // wbp_Latn_AU
- 0xC036494E54656C75llu, // wbq_Telu_IN
- 0xC436494E44657661llu, // wbr_Deva_IN
- 0xC97657464C61746Ellu, // wls_Latn_WF
- 0xA1B64B4D41726162llu, // wni_Arab_KM
- 0x776F534E4C61746Ellu, // wo_Latn_SN
- 0xB276494E44657661llu, // wtm_Deva_IN
- 0xD296434E48616E73llu, // wuu_Hans_CN
- 0xD41742524C61746Ellu, // xav_Latn_BR
- 0xC457545243617269llu, // xcr_Cari_TR
- 0x78685A414C61746Ellu, // xh_Latn_ZA
- 0x897754524C796369llu, // xlc_Lyci_TR
- 0x8D7754524C796469llu, // xld_Lydi_TR
- 0x9597474547656F72llu, // xmf_Geor_GE
- 0xB597434E4D616E69llu, // xmn_Mani_CN
- 0xC59753444D657263llu, // xmr_Merc_SD
- 0x81B753414E617262llu, // xna_Narb_SA
- 0xC5B7494E44657661llu, // xnr_Deva_IN
- 0x99D755474C61746Ellu, // xog_Latn_UG
- 0xC5F7495250727469llu, // xpr_Prti_IR
- 0x8257594553617262llu, // xsa_Sarb_YE
- 0xC6574E5044657661llu, // xsr_Deva_NP
- 0xB8184D5A4C61746Ellu, // yao_Latn_MZ
- 0xBC18464D4C61746Ellu, // yap_Latn_FM
- 0xD418434D4C61746Ellu, // yav_Latn_CM
- 0x8438434D4C61746Ellu, // ybb_Latn_CM
- 0x796F4E474C61746Ellu, // yo_Latn_NG
- 0xAE3842524C61746Ellu, // yrl_Latn_BR
- 0x82984D584C61746Ellu, // yua_Latn_MX
- 0x9298434E48616E73llu, // yue_Hans_CN
- 0x9298484B48616E74llu, // yue_Hant_HK
- 0x7A61434E4C61746Ellu, // za_Latn_CN
- 0x981953444C61746Ellu, // zag_Latn_SD
- 0xA4794B4D41726162llu, // zdj_Arab_KM
- 0x80994E4C4C61746Ellu, // zea_Latn_NL
- 0x9CD94D4154666E67llu, // zgh_Tfng_MA
- 0x7A685457426F706Fllu, // zh_Bopo_TW
- 0x7A68545748616E62llu, // zh_Hanb_TW
- 0x7A68434E48616E73llu, // zh_Hans_CN
- 0x7A68545748616E74llu, // zh_Hant_TW
- 0xB17954474C61746Ellu, // zlm_Latn_TG
- 0xA1994D594C61746Ellu, // zmi_Latn_MY
- 0x7A755A414C61746Ellu, // zu_Latn_ZA
- 0x833954524C61746Ellu, // zza_Latn_TR
+ 0x616145544C61746ELLU, // aa_Latn_ET
+ 0x616247454379726CLLU, // ab_Cyrl_GE
+ 0xC42047484C61746ELLU, // abr_Latn_GH
+ 0x904049444C61746ELLU, // ace_Latn_ID
+ 0x9C4055474C61746ELLU, // ach_Latn_UG
+ 0x806047484C61746ELLU, // ada_Latn_GH
+ 0xE06052554379726CLLU, // ady_Cyrl_RU
+ 0x6165495241767374LLU, // ae_Avst_IR
+ 0x8480544E41726162LLU, // aeb_Arab_TN
+ 0x61665A414C61746ELLU, // af_Latn_ZA
+ 0xC0C0434D4C61746ELLU, // agq_Latn_CM
+ 0xB8E0494E41686F6DLLU, // aho_Ahom_IN
+ 0x616B47484C61746ELLU, // ak_Latn_GH
+ 0xA940495158737578LLU, // akk_Xsux_IQ
+ 0xB560584B4C61746ELLU, // aln_Latn_XK
+ 0xCD6052554379726CLLU, // alt_Cyrl_RU
+ 0x616D455445746869LLU, // am_Ethi_ET
+ 0xB9804E474C61746ELLU, // amo_Latn_NG
+ 0xE5C049444C61746ELLU, // aoz_Latn_ID
+ 0x8DE0544741726162LLU, // apd_Arab_TG
+ 0x6172454741726162LLU, // ar_Arab_EG
+ 0x8A20495241726D69LLU, // arc_Armi_IR
+ 0x8A204A4F4E626174LLU, // arc_Nbat_JO
+ 0x8A20535950616C6DLLU, // arc_Palm_SY
+ 0xB620434C4C61746ELLU, // arn_Latn_CL
+ 0xBA20424F4C61746ELLU, // aro_Latn_BO
+ 0xC220445A41726162LLU, // arq_Arab_DZ
+ 0xE2204D4141726162LLU, // ary_Arab_MA
+ 0xE620454741726162LLU, // arz_Arab_EG
+ 0x6173494E42656E67LLU, // as_Beng_IN
+ 0x8240545A4C61746ELLU, // asa_Latn_TZ
+ 0x9240555353676E77LLU, // ase_Sgnw_US
+ 0xCE4045534C61746ELLU, // ast_Latn_ES
+ 0xA66043414C61746ELLU, // atj_Latn_CA
+ 0x617652554379726CLLU, // av_Cyrl_RU
+ 0x82C0494E44657661LLU, // awa_Deva_IN
+ 0x6179424F4C61746ELLU, // ay_Latn_BO
+ 0x617A495241726162LLU, // az_Arab_IR
+ 0x617A415A4C61746ELLU, // az_Latn_AZ
+ 0x626152554379726CLLU, // ba_Cyrl_RU
+ 0xAC01504B41726162LLU, // bal_Arab_PK
+ 0xB40149444C61746ELLU, // ban_Latn_ID
+ 0xBC014E5044657661LLU, // bap_Deva_NP
+ 0xC40141544C61746ELLU, // bar_Latn_AT
+ 0xC801434D4C61746ELLU, // bas_Latn_CM
+ 0xDC01434D42616D75LLU, // bax_Bamu_CM
+ 0x882149444C61746ELLU, // bbc_Latn_ID
+ 0xA421434D4C61746ELLU, // bbj_Latn_CM
+ 0xA04143494C61746ELLU, // bci_Latn_CI
+ 0x626542594379726CLLU, // be_Cyrl_BY
+ 0xA481534441726162LLU, // bej_Arab_SD
+ 0xB0815A4D4C61746ELLU, // bem_Latn_ZM
+ 0xD88149444C61746ELLU, // bew_Latn_ID
+ 0xE481545A4C61746ELLU, // bez_Latn_TZ
+ 0x8CA1434D4C61746ELLU, // bfd_Latn_CM
+ 0xC0A1494E54616D6CLLU, // bfq_Taml_IN
+ 0xCCA1504B41726162LLU, // bft_Arab_PK
+ 0xE0A1494E44657661LLU, // bfy_Deva_IN
+ 0x626742474379726CLLU, // bg_Cyrl_BG
+ 0x88C1494E44657661LLU, // bgc_Deva_IN
+ 0xB4C1504B41726162LLU, // bgn_Arab_PK
+ 0xDCC154524772656BLLU, // bgx_Grek_TR
+ 0x84E1494E44657661LLU, // bhb_Deva_IN
+ 0xA0E1494E44657661LLU, // bhi_Deva_IN
+ 0xA8E150484C61746ELLU, // bhk_Latn_PH
+ 0xB8E1494E44657661LLU, // bho_Deva_IN
+ 0x626956554C61746ELLU, // bi_Latn_VU
+ 0xA90150484C61746ELLU, // bik_Latn_PH
+ 0xB5014E474C61746ELLU, // bin_Latn_NG
+ 0xA521494E44657661LLU, // bjj_Deva_IN
+ 0xB52149444C61746ELLU, // bjn_Latn_ID
+ 0xB141434D4C61746ELLU, // bkm_Latn_CM
+ 0xD14150484C61746ELLU, // bku_Latn_PH
+ 0xCD61564E54617674LLU, // blt_Tavt_VN
+ 0x626D4D4C4C61746ELLU, // bm_Latn_ML
+ 0xC1814D4C4C61746ELLU, // bmq_Latn_ML
+ 0x626E424442656E67LLU, // bn_Beng_BD
+ 0x626F434E54696274LLU, // bo_Tibt_CN
+ 0xE1E1494E42656E67LLU, // bpy_Beng_IN
+ 0xA201495241726162LLU, // bqi_Arab_IR
+ 0xD60143494C61746ELLU, // bqv_Latn_CI
+ 0x627246524C61746ELLU, // br_Latn_FR
+ 0x8221494E44657661LLU, // bra_Deva_IN
+ 0x9E21504B41726162LLU, // brh_Arab_PK
+ 0xDE21494E44657661LLU, // brx_Deva_IN
+ 0x627342414C61746ELLU, // bs_Latn_BA
+ 0xC2414C5242617373LLU, // bsq_Bass_LR
+ 0xCA41434D4C61746ELLU, // bss_Latn_CM
+ 0xBA6150484C61746ELLU, // bto_Latn_PH
+ 0xD661504B44657661LLU, // btv_Deva_PK
+ 0x828152554379726CLLU, // bua_Cyrl_RU
+ 0x8A8159544C61746ELLU, // buc_Latn_YT
+ 0x9A8149444C61746ELLU, // bug_Latn_ID
+ 0xB281434D4C61746ELLU, // bum_Latn_CM
+ 0x86A147514C61746ELLU, // bvb_Latn_GQ
+ 0xB701455245746869LLU, // byn_Ethi_ER
+ 0xD701434D4C61746ELLU, // byv_Latn_CM
+ 0x93214D4C4C61746ELLU, // bze_Latn_ML
+ 0x636145534C61746ELLU, // ca_Latn_ES
+ 0x9C424E474C61746ELLU, // cch_Latn_NG
+ 0xBC42494E42656E67LLU, // ccp_Beng_IN
+ 0xBC42424443616B6DLLU, // ccp_Cakm_BD
+ 0x636552554379726CLLU, // ce_Cyrl_RU
+ 0x848250484C61746ELLU, // ceb_Latn_PH
+ 0x98C255474C61746ELLU, // cgg_Latn_UG
+ 0x636847554C61746ELLU, // ch_Latn_GU
+ 0xA8E2464D4C61746ELLU, // chk_Latn_FM
+ 0xB0E252554379726CLLU, // chm_Cyrl_RU
+ 0xB8E255534C61746ELLU, // cho_Latn_US
+ 0xBCE243414C61746ELLU, // chp_Latn_CA
+ 0xC4E2555343686572LLU, // chr_Cher_US
+ 0x81224B4841726162LLU, // cja_Arab_KH
+ 0xB122564E4368616DLLU, // cjm_Cham_VN
+ 0x8542495141726162LLU, // ckb_Arab_IQ
+ 0x636F46524C61746ELLU, // co_Latn_FR
+ 0xBDC24547436F7074LLU, // cop_Copt_EG
+ 0xC9E250484C61746ELLU, // cps_Latn_PH
+ 0x6372434143616E73LLU, // cr_Cans_CA
+ 0xA622434143616E73LLU, // crj_Cans_CA
+ 0xAA22434143616E73LLU, // crk_Cans_CA
+ 0xAE22434143616E73LLU, // crl_Cans_CA
+ 0xB222434143616E73LLU, // crm_Cans_CA
+ 0xCA2253434C61746ELLU, // crs_Latn_SC
+ 0x6373435A4C61746ELLU, // cs_Latn_CZ
+ 0x8642504C4C61746ELLU, // csb_Latn_PL
+ 0xDA42434143616E73LLU, // csw_Cans_CA
+ 0x8E624D4D50617563LLU, // ctd_Pauc_MM
+ 0x637552554379726CLLU, // cu_Cyrl_RU
+ 0x63754247476C6167LLU, // cu_Glag_BG
+ 0x637652554379726CLLU, // cv_Cyrl_RU
+ 0x637947424C61746ELLU, // cy_Latn_GB
+ 0x6461444B4C61746ELLU, // da_Latn_DK
+ 0xA80355534C61746ELLU, // dak_Latn_US
+ 0xC40352554379726CLLU, // dar_Cyrl_RU
+ 0xD4034B454C61746ELLU, // dav_Latn_KE
+ 0x8843494E41726162LLU, // dcc_Arab_IN
+ 0x646544454C61746ELLU, // de_Latn_DE
+ 0xB48343414C61746ELLU, // den_Latn_CA
+ 0xC4C343414C61746ELLU, // dgr_Latn_CA
+ 0x91234E454C61746ELLU, // dje_Latn_NE
+ 0xA5A343494C61746ELLU, // dnj_Latn_CI
+ 0xA1C3494E41726162LLU, // doi_Arab_IN
+ 0x864344454C61746ELLU, // dsb_Latn_DE
+ 0xB2634D4C4C61746ELLU, // dtm_Latn_ML
+ 0xBE634D594C61746ELLU, // dtp_Latn_MY
+ 0xE2634E5044657661LLU, // dty_Deva_NP
+ 0x8283434D4C61746ELLU, // dua_Latn_CM
+ 0x64764D5654686161LLU, // dv_Thaa_MV
+ 0xBB03534E4C61746ELLU, // dyo_Latn_SN
+ 0xD30342464C61746ELLU, // dyu_Latn_BF
+ 0x647A425454696274LLU, // dz_Tibt_BT
+ 0xD0244B454C61746ELLU, // ebu_Latn_KE
+ 0x656547484C61746ELLU, // ee_Latn_GH
+ 0xA0A44E474C61746ELLU, // efi_Latn_NG
+ 0xACC449544C61746ELLU, // egl_Latn_IT
+ 0xE0C4454745677970LLU, // egy_Egyp_EG
+ 0xE1444D4D4B616C69LLU, // eky_Kali_MM
+ 0x656C47524772656BLLU, // el_Grek_GR
+ 0x656E47424C61746ELLU, // en_Latn_GB
+ 0x656E55534C61746ELLU, // en_Latn_US
+ 0x656E474253686177LLU, // en_Shaw_GB
+ 0x657345534C61746ELLU, // es_Latn_ES
+ 0x65734D584C61746ELLU, // es_Latn_MX
+ 0x657355534C61746ELLU, // es_Latn_US
+ 0xD24455534C61746ELLU, // esu_Latn_US
+ 0x657445454C61746ELLU, // et_Latn_EE
+ 0xCE6449544974616CLLU, // ett_Ital_IT
+ 0x657545534C61746ELLU, // eu_Latn_ES
+ 0xBAC4434D4C61746ELLU, // ewo_Latn_CM
+ 0xCEE445534C61746ELLU, // ext_Latn_ES
+ 0x6661495241726162LLU, // fa_Arab_IR
+ 0xB40547514C61746ELLU, // fan_Latn_GQ
+ 0x6666474E41646C6DLLU, // ff_Adlm_GN
+ 0x6666534E4C61746ELLU, // ff_Latn_SN
+ 0xB0A54D4C4C61746ELLU, // ffm_Latn_ML
+ 0x666946494C61746ELLU, // fi_Latn_FI
+ 0x8105534441726162LLU, // fia_Arab_SD
+ 0xAD0550484C61746ELLU, // fil_Latn_PH
+ 0xCD0553454C61746ELLU, // fit_Latn_SE
+ 0x666A464A4C61746ELLU, // fj_Latn_FJ
+ 0x666F464F4C61746ELLU, // fo_Latn_FO
+ 0xB5C5424A4C61746ELLU, // fon_Latn_BJ
+ 0x667246524C61746ELLU, // fr_Latn_FR
+ 0x8A2555534C61746ELLU, // frc_Latn_US
+ 0xBE2546524C61746ELLU, // frp_Latn_FR
+ 0xC62544454C61746ELLU, // frr_Latn_DE
+ 0xCA2544454C61746ELLU, // frs_Latn_DE
+ 0x8685434D41726162LLU, // fub_Arab_CM
+ 0x8E8557464C61746ELLU, // fud_Latn_WF
+ 0x9685474E4C61746ELLU, // fuf_Latn_GN
+ 0xC2854E454C61746ELLU, // fuq_Latn_NE
+ 0xC68549544C61746ELLU, // fur_Latn_IT
+ 0xD6854E474C61746ELLU, // fuv_Latn_NG
+ 0xC6A553444C61746ELLU, // fvr_Latn_SD
+ 0x66794E4C4C61746ELLU, // fy_Latn_NL
+ 0x676149454C61746ELLU, // ga_Latn_IE
+ 0x800647484C61746ELLU, // gaa_Latn_GH
+ 0x98064D444C61746ELLU, // gag_Latn_MD
+ 0xB406434E48616E73LLU, // gan_Hans_CN
+ 0xE00649444C61746ELLU, // gay_Latn_ID
+ 0xB026494E44657661LLU, // gbm_Deva_IN
+ 0xE426495241726162LLU, // gbz_Arab_IR
+ 0xC44647464C61746ELLU, // gcr_Latn_GF
+ 0x676447424C61746ELLU, // gd_Latn_GB
+ 0xE486455445746869LLU, // gez_Ethi_ET
+ 0xB4C64E5044657661LLU, // ggn_Deva_NP
+ 0xAD064B494C61746ELLU, // gil_Latn_KI
+ 0xA926504B41726162LLU, // gjk_Arab_PK
+ 0xD126504B41726162LLU, // gju_Arab_PK
+ 0x676C45534C61746ELLU, // gl_Latn_ES
+ 0xA966495241726162LLU, // glk_Arab_IR
+ 0x676E50594C61746ELLU, // gn_Latn_PY
+ 0xB1C6494E44657661LLU, // gom_Deva_IN
+ 0xB5C6494E54656C75LLU, // gon_Telu_IN
+ 0xC5C649444C61746ELLU, // gor_Latn_ID
+ 0xC9C64E4C4C61746ELLU, // gos_Latn_NL
+ 0xCDC65541476F7468LLU, // got_Goth_UA
+ 0x8A26435943707274LLU, // grc_Cprt_CY
+ 0x8A2647524C696E62LLU, // grc_Linb_GR
+ 0xCE26494E42656E67LLU, // grt_Beng_IN
+ 0xDA4643484C61746ELLU, // gsw_Latn_CH
+ 0x6775494E47756A72LLU, // gu_Gujr_IN
+ 0x868642524C61746ELLU, // gub_Latn_BR
+ 0x8A86434F4C61746ELLU, // guc_Latn_CO
+ 0xC68647484C61746ELLU, // gur_Latn_GH
+ 0xE6864B454C61746ELLU, // guz_Latn_KE
+ 0x6776494D4C61746ELLU, // gv_Latn_IM
+ 0xC6A64E5044657661LLU, // gvr_Deva_NP
+ 0xA2C643414C61746ELLU, // gwi_Latn_CA
+ 0x68614E474C61746ELLU, // ha_Latn_NG
+ 0xA807434E48616E73LLU, // hak_Hans_CN
+ 0xD80755534C61746ELLU, // haw_Latn_US
+ 0xE407414641726162LLU, // haz_Arab_AF
+ 0x6865494C48656272LLU, // he_Hebr_IL
+ 0x6869494E44657661LLU, // hi_Deva_IN
+ 0x9507464A4C61746ELLU, // hif_Latn_FJ
+ 0xAD0750484C61746ELLU, // hil_Latn_PH
+ 0xD1675452486C7577LLU, // hlu_Hluw_TR
+ 0x8D87434E506C7264LLU, // hmd_Plrd_CN
+ 0x8DA7504B41726162LLU, // hnd_Arab_PK
+ 0x91A7494E44657661LLU, // hne_Deva_IN
+ 0xA5A74C41486D6E67LLU, // hnj_Hmng_LA
+ 0xB5A750484C61746ELLU, // hnn_Latn_PH
+ 0xB9A7504B41726162LLU, // hno_Arab_PK
+ 0x686F50474C61746ELLU, // ho_Latn_PG
+ 0x89C7494E44657661LLU, // hoc_Deva_IN
+ 0xA5C7494E44657661LLU, // hoj_Deva_IN
+ 0x687248524C61746ELLU, // hr_Latn_HR
+ 0x864744454C61746ELLU, // hsb_Latn_DE
+ 0xB647434E48616E73LLU, // hsn_Hans_CN
+ 0x687448544C61746ELLU, // ht_Latn_HT
+ 0x687548554C61746ELLU, // hu_Latn_HU
+ 0x6879414D41726D6ELLU, // hy_Armn_AM
+ 0x687A4E414C61746ELLU, // hz_Latn_NA
+ 0x696146524C61746ELLU, // ia_Latn_FR
+ 0x80284D594C61746ELLU, // iba_Latn_MY
+ 0x84284E474C61746ELLU, // ibb_Latn_NG
+ 0x696449444C61746ELLU, // id_Latn_ID
+ 0x69674E474C61746ELLU, // ig_Latn_NG
+ 0x6969434E59696969LLU, // ii_Yiii_CN
+ 0x696B55534C61746ELLU, // ik_Latn_US
+ 0xCD4843414C61746ELLU, // ikt_Latn_CA
+ 0xB96850484C61746ELLU, // ilo_Latn_PH
+ 0x696E49444C61746ELLU, // in_Latn_ID
+ 0x9DA852554379726CLLU, // inh_Cyrl_RU
+ 0x697349534C61746ELLU, // is_Latn_IS
+ 0x697449544C61746ELLU, // it_Latn_IT
+ 0x6975434143616E73LLU, // iu_Cans_CA
+ 0x6977494C48656272LLU, // iw_Hebr_IL
+ 0x9F2852554C61746ELLU, // izh_Latn_RU
+ 0x6A614A504A70616ELLU, // ja_Jpan_JP
+ 0xB0094A4D4C61746ELLU, // jam_Latn_JM
+ 0xB8C9434D4C61746ELLU, // jgo_Latn_CM
+ 0x8989545A4C61746ELLU, // jmc_Latn_TZ
+ 0xAD894E5044657661LLU, // jml_Deva_NP
+ 0xCE89444B4C61746ELLU, // jut_Latn_DK
+ 0x6A7649444C61746ELLU, // jv_Latn_ID
+ 0x6A7749444C61746ELLU, // jw_Latn_ID
+ 0x6B61474547656F72LLU, // ka_Geor_GE
+ 0x800A555A4379726CLLU, // kaa_Cyrl_UZ
+ 0x840A445A4C61746ELLU, // kab_Latn_DZ
+ 0x880A4D4D4C61746ELLU, // kac_Latn_MM
+ 0xA40A4E474C61746ELLU, // kaj_Latn_NG
+ 0xB00A4B454C61746ELLU, // kam_Latn_KE
+ 0xB80A4D4C4C61746ELLU, // kao_Latn_ML
+ 0x8C2A52554379726CLLU, // kbd_Cyrl_RU
+ 0xE02A4E4541726162LLU, // kby_Arab_NE
+ 0x984A4E474C61746ELLU, // kcg_Latn_NG
+ 0xA84A5A574C61746ELLU, // kck_Latn_ZW
+ 0x906A545A4C61746ELLU, // kde_Latn_TZ
+ 0x9C6A544741726162LLU, // kdh_Arab_TG
+ 0xCC6A544854686169LLU, // kdt_Thai_TH
+ 0x808A43564C61746ELLU, // kea_Latn_CV
+ 0xB48A434D4C61746ELLU, // ken_Latn_CM
+ 0xB8AA43494C61746ELLU, // kfo_Latn_CI
+ 0xC4AA494E44657661LLU, // kfr_Deva_IN
+ 0xE0AA494E44657661LLU, // kfy_Deva_IN
+ 0x6B6743444C61746ELLU, // kg_Latn_CD
+ 0x90CA49444C61746ELLU, // kge_Latn_ID
+ 0xBCCA42524C61746ELLU, // kgp_Latn_BR
+ 0x80EA494E4C61746ELLU, // kha_Latn_IN
+ 0x84EA434E54616C75LLU, // khb_Talu_CN
+ 0xB4EA494E44657661LLU, // khn_Deva_IN
+ 0xC0EA4D4C4C61746ELLU, // khq_Latn_ML
+ 0xCCEA494E4D796D72LLU, // kht_Mymr_IN
+ 0xD8EA504B41726162LLU, // khw_Arab_PK
+ 0x6B694B454C61746ELLU, // ki_Latn_KE
+ 0xD10A54524C61746ELLU, // kiu_Latn_TR
+ 0x6B6A4E414C61746ELLU, // kj_Latn_NA
+ 0x992A4C414C616F6FLLU, // kjg_Laoo_LA
+ 0x6B6B434E41726162LLU, // kk_Arab_CN
+ 0x6B6B4B5A4379726CLLU, // kk_Cyrl_KZ
+ 0xA54A434D4C61746ELLU, // kkj_Latn_CM
+ 0x6B6C474C4C61746ELLU, // kl_Latn_GL
+ 0xB56A4B454C61746ELLU, // kln_Latn_KE
+ 0x6B6D4B484B686D72LLU, // km_Khmr_KH
+ 0x858A414F4C61746ELLU, // kmb_Latn_AO
+ 0x6B6E494E4B6E6461LLU, // kn_Knda_IN
+ 0x6B6F4B524B6F7265LLU, // ko_Kore_KR
+ 0xA1CA52554379726CLLU, // koi_Cyrl_RU
+ 0xA9CA494E44657661LLU, // kok_Deva_IN
+ 0xC9CA464D4C61746ELLU, // kos_Latn_FM
+ 0x91EA4C524C61746ELLU, // kpe_Latn_LR
+ 0x8A2A52554379726CLLU, // krc_Cyrl_RU
+ 0xA22A534C4C61746ELLU, // kri_Latn_SL
+ 0xA62A50484C61746ELLU, // krj_Latn_PH
+ 0xAE2A52554C61746ELLU, // krl_Latn_RU
+ 0xD22A494E44657661LLU, // kru_Deva_IN
+ 0x6B73494E41726162LLU, // ks_Arab_IN
+ 0x864A545A4C61746ELLU, // ksb_Latn_TZ
+ 0x964A434D4C61746ELLU, // ksf_Latn_CM
+ 0x9E4A44454C61746ELLU, // ksh_Latn_DE
+ 0x6B75495141726162LLU, // ku_Arab_IQ
+ 0x6B7554524C61746ELLU, // ku_Latn_TR
+ 0xB28A52554379726CLLU, // kum_Cyrl_RU
+ 0x6B7652554379726CLLU, // kv_Cyrl_RU
+ 0xC6AA49444C61746ELLU, // kvr_Latn_ID
+ 0xDEAA504B41726162LLU, // kvx_Arab_PK
+ 0x6B7747424C61746ELLU, // kw_Latn_GB
+ 0xB2EA544854686169LLU, // kxm_Thai_TH
+ 0xBEEA504B41726162LLU, // kxp_Arab_PK
+ 0x6B79434E41726162LLU, // ky_Arab_CN
+ 0x6B794B474379726CLLU, // ky_Cyrl_KG
+ 0x6B7954524C61746ELLU, // ky_Latn_TR
+ 0x6C6156414C61746ELLU, // la_Latn_VA
+ 0x840B47524C696E61LLU, // lab_Lina_GR
+ 0x8C0B494C48656272LLU, // lad_Hebr_IL
+ 0x980B545A4C61746ELLU, // lag_Latn_TZ
+ 0x9C0B504B41726162LLU, // lah_Arab_PK
+ 0xA40B55474C61746ELLU, // laj_Latn_UG
+ 0x6C624C554C61746ELLU, // lb_Latn_LU
+ 0x902B52554379726CLLU, // lbe_Cyrl_RU
+ 0xD82B49444C61746ELLU, // lbw_Latn_ID
+ 0xBC4B434E54686169LLU, // lcp_Thai_CN
+ 0xBC8B494E4C657063LLU, // lep_Lepc_IN
+ 0xE48B52554379726CLLU, // lez_Cyrl_RU
+ 0x6C6755474C61746ELLU, // lg_Latn_UG
+ 0x6C694E4C4C61746ELLU, // li_Latn_NL
+ 0x950B4E5044657661LLU, // lif_Deva_NP
+ 0x950B494E4C696D62LLU, // lif_Limb_IN
+ 0xA50B49544C61746ELLU, // lij_Latn_IT
+ 0xC90B434E4C697375LLU, // lis_Lisu_CN
+ 0xBD2B49444C61746ELLU, // ljp_Latn_ID
+ 0xA14B495241726162LLU, // lki_Arab_IR
+ 0xCD4B55534C61746ELLU, // lkt_Latn_US
+ 0xB58B494E54656C75LLU, // lmn_Telu_IN
+ 0xB98B49544C61746ELLU, // lmo_Latn_IT
+ 0x6C6E43444C61746ELLU, // ln_Latn_CD
+ 0x6C6F4C414C616F6FLLU, // lo_Laoo_LA
+ 0xADCB43444C61746ELLU, // lol_Latn_CD
+ 0xE5CB5A4D4C61746ELLU, // loz_Latn_ZM
+ 0x8A2B495241726162LLU, // lrc_Arab_IR
+ 0x6C744C544C61746ELLU, // lt_Latn_LT
+ 0x9A6B4C564C61746ELLU, // ltg_Latn_LV
+ 0x6C7543444C61746ELLU, // lu_Latn_CD
+ 0x828B43444C61746ELLU, // lua_Latn_CD
+ 0xBA8B4B454C61746ELLU, // luo_Latn_KE
+ 0xE28B4B454C61746ELLU, // luy_Latn_KE
+ 0xE68B495241726162LLU, // luz_Arab_IR
+ 0x6C764C564C61746ELLU, // lv_Latn_LV
+ 0xAECB544854686169LLU, // lwl_Thai_TH
+ 0x9F2B434E48616E73LLU, // lzh_Hans_CN
+ 0xE72B54524C61746ELLU, // lzz_Latn_TR
+ 0x8C0C49444C61746ELLU, // mad_Latn_ID
+ 0x940C434D4C61746ELLU, // maf_Latn_CM
+ 0x980C494E44657661LLU, // mag_Deva_IN
+ 0xA00C494E44657661LLU, // mai_Deva_IN
+ 0xA80C49444C61746ELLU, // mak_Latn_ID
+ 0xB40C474D4C61746ELLU, // man_Latn_GM
+ 0xB40C474E4E6B6F6FLLU, // man_Nkoo_GN
+ 0xC80C4B454C61746ELLU, // mas_Latn_KE
+ 0xE40C4D584C61746ELLU, // maz_Latn_MX
+ 0x946C52554379726CLLU, // mdf_Cyrl_RU
+ 0x9C6C50484C61746ELLU, // mdh_Latn_PH
+ 0xC46C49444C61746ELLU, // mdr_Latn_ID
+ 0xB48C534C4C61746ELLU, // men_Latn_SL
+ 0xC48C4B454C61746ELLU, // mer_Latn_KE
+ 0x80AC544841726162LLU, // mfa_Arab_TH
+ 0x90AC4D554C61746ELLU, // mfe_Latn_MU
+ 0x6D674D474C61746ELLU, // mg_Latn_MG
+ 0x9CCC4D5A4C61746ELLU, // mgh_Latn_MZ
+ 0xB8CC434D4C61746ELLU, // mgo_Latn_CM
+ 0xBCCC4E5044657661LLU, // mgp_Deva_NP
+ 0xE0CC545A4C61746ELLU, // mgy_Latn_TZ
+ 0x6D684D484C61746ELLU, // mh_Latn_MH
+ 0x6D694E5A4C61746ELLU, // mi_Latn_NZ
+ 0xB50C49444C61746ELLU, // min_Latn_ID
+ 0xC90C495148617472LLU, // mis_Hatr_IQ
+ 0x6D6B4D4B4379726CLLU, // mk_Cyrl_MK
+ 0x6D6C494E4D6C796DLLU, // ml_Mlym_IN
+ 0xC96C53444C61746ELLU, // mls_Latn_SD
+ 0x6D6E4D4E4379726CLLU, // mn_Cyrl_MN
+ 0x6D6E434E4D6F6E67LLU, // mn_Mong_CN
+ 0xA1AC494E42656E67LLU, // mni_Beng_IN
+ 0xD9AC4D4D4D796D72LLU, // mnw_Mymr_MM
+ 0x91CC43414C61746ELLU, // moe_Latn_CA
+ 0x9DCC43414C61746ELLU, // moh_Latn_CA
+ 0xC9CC42464C61746ELLU, // mos_Latn_BF
+ 0x6D72494E44657661LLU, // mr_Deva_IN
+ 0x8E2C4E5044657661LLU, // mrd_Deva_NP
+ 0xA62C52554379726CLLU, // mrj_Cyrl_RU
+ 0xBA2C42444D726F6FLLU, // mro_Mroo_BD
+ 0x6D734D594C61746ELLU, // ms_Latn_MY
+ 0x6D744D544C61746ELLU, // mt_Latn_MT
+ 0xC66C494E44657661LLU, // mtr_Deva_IN
+ 0x828C434D4C61746ELLU, // mua_Latn_CM
+ 0xCA8C55534C61746ELLU, // mus_Latn_US
+ 0xE2AC504B41726162LLU, // mvy_Arab_PK
+ 0xAACC4D4C4C61746ELLU, // mwk_Latn_ML
+ 0xC6CC494E44657661LLU, // mwr_Deva_IN
+ 0xD6CC49444C61746ELLU, // mwv_Latn_ID
+ 0x8AEC5A574C61746ELLU, // mxc_Latn_ZW
+ 0x6D794D4D4D796D72LLU, // my_Mymr_MM
+ 0xD70C52554379726CLLU, // myv_Cyrl_RU
+ 0xDF0C55474C61746ELLU, // myx_Latn_UG
+ 0xE70C49524D616E64LLU, // myz_Mand_IR
+ 0xB72C495241726162LLU, // mzn_Arab_IR
+ 0x6E614E524C61746ELLU, // na_Latn_NR
+ 0xB40D434E48616E73LLU, // nan_Hans_CN
+ 0xBC0D49544C61746ELLU, // nap_Latn_IT
+ 0xC00D4E414C61746ELLU, // naq_Latn_NA
+ 0x6E624E4F4C61746ELLU, // nb_Latn_NO
+ 0x9C4D4D584C61746ELLU, // nch_Latn_MX
+ 0x6E645A574C61746ELLU, // nd_Latn_ZW
+ 0x886D4D5A4C61746ELLU, // ndc_Latn_MZ
+ 0xC86D44454C61746ELLU, // nds_Latn_DE
+ 0x6E654E5044657661LLU, // ne_Deva_NP
+ 0xD88D4E5044657661LLU, // new_Deva_NP
+ 0x6E674E414C61746ELLU, // ng_Latn_NA
+ 0xACCD4D5A4C61746ELLU, // ngl_Latn_MZ
+ 0x90ED4D584C61746ELLU, // nhe_Latn_MX
+ 0xD8ED4D584C61746ELLU, // nhw_Latn_MX
+ 0xA50D49444C61746ELLU, // nij_Latn_ID
+ 0xD10D4E554C61746ELLU, // niu_Latn_NU
+ 0xB92D494E4C61746ELLU, // njo_Latn_IN
+ 0x6E6C4E4C4C61746ELLU, // nl_Latn_NL
+ 0x998D434D4C61746ELLU, // nmg_Latn_CM
+ 0x6E6E4E4F4C61746ELLU, // nn_Latn_NO
+ 0x9DAD434D4C61746ELLU, // nnh_Latn_CM
+ 0x6E6F4E4F4C61746ELLU, // no_Latn_NO
+ 0x8DCD54484C616E61LLU, // nod_Lana_TH
+ 0x91CD494E44657661LLU, // noe_Deva_IN
+ 0xB5CD534552756E72LLU, // non_Runr_SE
+ 0xBA0D474E4E6B6F6FLLU, // nqo_Nkoo_GN
+ 0x6E725A414C61746ELLU, // nr_Latn_ZA
+ 0xAA4D434143616E73LLU, // nsk_Cans_CA
+ 0xBA4D5A414C61746ELLU, // nso_Latn_ZA
+ 0xCA8D53534C61746ELLU, // nus_Latn_SS
+ 0x6E7655534C61746ELLU, // nv_Latn_US
+ 0xC2ED434E4C61746ELLU, // nxq_Latn_CN
+ 0x6E794D574C61746ELLU, // ny_Latn_MW
+ 0xB30D545A4C61746ELLU, // nym_Latn_TZ
+ 0xB70D55474C61746ELLU, // nyn_Latn_UG
+ 0xA32D47484C61746ELLU, // nzi_Latn_GH
+ 0x6F6346524C61746ELLU, // oc_Latn_FR
+ 0x6F6D45544C61746ELLU, // om_Latn_ET
+ 0x6F72494E4F727961LLU, // or_Orya_IN
+ 0x6F7347454379726CLLU, // os_Cyrl_GE
+ 0x824E55534F736765LLU, // osa_Osge_US
+ 0xAA6E4D4E4F726B68LLU, // otk_Orkh_MN
+ 0x7061504B41726162LLU, // pa_Arab_PK
+ 0x7061494E47757275LLU, // pa_Guru_IN
+ 0x980F50484C61746ELLU, // pag_Latn_PH
+ 0xAC0F495250686C69LLU, // pal_Phli_IR
+ 0xAC0F434E50686C70LLU, // pal_Phlp_CN
+ 0xB00F50484C61746ELLU, // pam_Latn_PH
+ 0xBC0F41574C61746ELLU, // pap_Latn_AW
+ 0xD00F50574C61746ELLU, // pau_Latn_PW
+ 0x8C4F46524C61746ELLU, // pcd_Latn_FR
+ 0xB04F4E474C61746ELLU, // pcm_Latn_NG
+ 0x886F55534C61746ELLU, // pdc_Latn_US
+ 0xCC6F43414C61746ELLU, // pdt_Latn_CA
+ 0xB88F49525870656FLLU, // peo_Xpeo_IR
+ 0xACAF44454C61746ELLU, // pfl_Latn_DE
+ 0xB4EF4C4250686E78LLU, // phn_Phnx_LB
+ 0x814F494E42726168LLU, // pka_Brah_IN
+ 0xB94F4B454C61746ELLU, // pko_Latn_KE
+ 0x706C504C4C61746ELLU, // pl_Latn_PL
+ 0xC98F49544C61746ELLU, // pms_Latn_IT
+ 0xCDAF47524772656BLLU, // pnt_Grek_GR
+ 0xB5CF464D4C61746ELLU, // pon_Latn_FM
+ 0x822F504B4B686172LLU, // pra_Khar_PK
+ 0x8E2F495241726162LLU, // prd_Arab_IR
+ 0x7073414641726162LLU, // ps_Arab_AF
+ 0x707442524C61746ELLU, // pt_Latn_BR
+ 0xD28F47414C61746ELLU, // puu_Latn_GA
+ 0x717550454C61746ELLU, // qu_Latn_PE
+ 0x8A9047544C61746ELLU, // quc_Latn_GT
+ 0x9A9045434C61746ELLU, // qug_Latn_EC
+ 0xA411494E44657661LLU, // raj_Deva_IN
+ 0x945152454C61746ELLU, // rcf_Latn_RE
+ 0xA49149444C61746ELLU, // rej_Latn_ID
+ 0xB4D149544C61746ELLU, // rgn_Latn_IT
+ 0x8111494E4C61746ELLU, // ria_Latn_IN
+ 0x95114D4154666E67LLU, // rif_Tfng_MA
+ 0xC9314E5044657661LLU, // rjs_Deva_NP
+ 0xCD51424442656E67LLU, // rkt_Beng_BD
+ 0x726D43484C61746ELLU, // rm_Latn_CH
+ 0x959146494C61746ELLU, // rmf_Latn_FI
+ 0xB99143484C61746ELLU, // rmo_Latn_CH
+ 0xCD91495241726162LLU, // rmt_Arab_IR
+ 0xD19153454C61746ELLU, // rmu_Latn_SE
+ 0x726E42494C61746ELLU, // rn_Latn_BI
+ 0x99B14D5A4C61746ELLU, // rng_Latn_MZ
+ 0x726F524F4C61746ELLU, // ro_Latn_RO
+ 0x85D149444C61746ELLU, // rob_Latn_ID
+ 0x95D1545A4C61746ELLU, // rof_Latn_TZ
+ 0xB271464A4C61746ELLU, // rtm_Latn_FJ
+ 0x727552554379726CLLU, // ru_Cyrl_RU
+ 0x929155414379726CLLU, // rue_Cyrl_UA
+ 0x9A9153424C61746ELLU, // rug_Latn_SB
+ 0x727752574C61746ELLU, // rw_Latn_RW
+ 0xAAD1545A4C61746ELLU, // rwk_Latn_TZ
+ 0xD3114A504B616E61LLU, // ryu_Kana_JP
+ 0x7361494E44657661LLU, // sa_Deva_IN
+ 0x941247484C61746ELLU, // saf_Latn_GH
+ 0x9C1252554379726CLLU, // sah_Cyrl_RU
+ 0xC0124B454C61746ELLU, // saq_Latn_KE
+ 0xC81249444C61746ELLU, // sas_Latn_ID
+ 0xCC12494E4C61746ELLU, // sat_Latn_IN
+ 0xE412494E53617572LLU, // saz_Saur_IN
+ 0xBC32545A4C61746ELLU, // sbp_Latn_TZ
+ 0x736349544C61746ELLU, // sc_Latn_IT
+ 0xA852494E44657661LLU, // sck_Deva_IN
+ 0xB45249544C61746ELLU, // scn_Latn_IT
+ 0xB85247424C61746ELLU, // sco_Latn_GB
+ 0xC85243414C61746ELLU, // scs_Latn_CA
+ 0x7364504B41726162LLU, // sd_Arab_PK
+ 0x7364494E44657661LLU, // sd_Deva_IN
+ 0x7364494E4B686F6ALLU, // sd_Khoj_IN
+ 0x7364494E53696E64LLU, // sd_Sind_IN
+ 0x887249544C61746ELLU, // sdc_Latn_IT
+ 0x9C72495241726162LLU, // sdh_Arab_IR
+ 0x73654E4F4C61746ELLU, // se_Latn_NO
+ 0x949243494C61746ELLU, // sef_Latn_CI
+ 0x9C924D5A4C61746ELLU, // seh_Latn_MZ
+ 0xA0924D584C61746ELLU, // sei_Latn_MX
+ 0xC8924D4C4C61746ELLU, // ses_Latn_ML
+ 0x736743464C61746ELLU, // sg_Latn_CF
+ 0x80D249454F67616DLLU, // sga_Ogam_IE
+ 0xC8D24C544C61746ELLU, // sgs_Latn_LT
+ 0xA0F24D4154666E67LLU, // shi_Tfng_MA
+ 0xB4F24D4D4D796D72LLU, // shn_Mymr_MM
+ 0x73694C4B53696E68LLU, // si_Sinh_LK
+ 0x8D1245544C61746ELLU, // sid_Latn_ET
+ 0x736B534B4C61746ELLU, // sk_Latn_SK
+ 0xC552504B41726162LLU, // skr_Arab_PK
+ 0x736C53494C61746ELLU, // sl_Latn_SI
+ 0xA172504C4C61746ELLU, // sli_Latn_PL
+ 0xE17249444C61746ELLU, // sly_Latn_ID
+ 0x736D57534C61746ELLU, // sm_Latn_WS
+ 0x819253454C61746ELLU, // sma_Latn_SE
+ 0xA59253454C61746ELLU, // smj_Latn_SE
+ 0xB59246494C61746ELLU, // smn_Latn_FI
+ 0xBD92494C53616D72LLU, // smp_Samr_IL
+ 0xC99246494C61746ELLU, // sms_Latn_FI
+ 0x736E5A574C61746ELLU, // sn_Latn_ZW
+ 0xA9B24D4C4C61746ELLU, // snk_Latn_ML
+ 0x736F534F4C61746ELLU, // so_Latn_SO
+ 0xD1D2544854686169LLU, // sou_Thai_TH
+ 0x7371414C4C61746ELLU, // sq_Latn_AL
+ 0x737252534379726CLLU, // sr_Cyrl_RS
+ 0x737252534C61746ELLU, // sr_Latn_RS
+ 0x8632494E536F7261LLU, // srb_Sora_IN
+ 0xB63253524C61746ELLU, // srn_Latn_SR
+ 0xC632534E4C61746ELLU, // srr_Latn_SN
+ 0xDE32494E44657661LLU, // srx_Deva_IN
+ 0x73735A414C61746ELLU, // ss_Latn_ZA
+ 0xE25245524C61746ELLU, // ssy_Latn_ER
+ 0x73745A414C61746ELLU, // st_Latn_ZA
+ 0xC27244454C61746ELLU, // stq_Latn_DE
+ 0x737549444C61746ELLU, // su_Latn_ID
+ 0xAA92545A4C61746ELLU, // suk_Latn_TZ
+ 0xCA92474E4C61746ELLU, // sus_Latn_GN
+ 0x737653454C61746ELLU, // sv_Latn_SE
+ 0x7377545A4C61746ELLU, // sw_Latn_TZ
+ 0x86D2595441726162LLU, // swb_Arab_YT
+ 0x8AD243444C61746ELLU, // swc_Latn_CD
+ 0x9AD244454C61746ELLU, // swg_Latn_DE
+ 0xD6D2494E44657661LLU, // swv_Deva_IN
+ 0xB6F249444C61746ELLU, // sxn_Latn_ID
+ 0xAF12424442656E67LLU, // syl_Beng_BD
+ 0xC712495153797263LLU, // syr_Syrc_IQ
+ 0xAF32504C4C61746ELLU, // szl_Latn_PL
+ 0x7461494E54616D6CLLU, // ta_Taml_IN
+ 0xA4134E5044657661LLU, // taj_Deva_NP
+ 0xD83350484C61746ELLU, // tbw_Latn_PH
+ 0xE053494E4B6E6461LLU, // tcy_Knda_IN
+ 0x8C73434E54616C65LLU, // tdd_Tale_CN
+ 0x98734E5044657661LLU, // tdg_Deva_NP
+ 0x9C734E5044657661LLU, // tdh_Deva_NP
+ 0x7465494E54656C75LLU, // te_Telu_IN
+ 0xB093534C4C61746ELLU, // tem_Latn_SL
+ 0xB89355474C61746ELLU, // teo_Latn_UG
+ 0xCC93544C4C61746ELLU, // tet_Latn_TL
+ 0x7467504B41726162LLU, // tg_Arab_PK
+ 0x7467544A4379726CLLU, // tg_Cyrl_TJ
+ 0x7468544854686169LLU, // th_Thai_TH
+ 0xACF34E5044657661LLU, // thl_Deva_NP
+ 0xC0F34E5044657661LLU, // thq_Deva_NP
+ 0xC4F34E5044657661LLU, // thr_Deva_NP
+ 0x7469455445746869LLU, // ti_Ethi_ET
+ 0x9913455245746869LLU, // tig_Ethi_ER
+ 0xD5134E474C61746ELLU, // tiv_Latn_NG
+ 0x746B544D4C61746ELLU, // tk_Latn_TM
+ 0xAD53544B4C61746ELLU, // tkl_Latn_TK
+ 0xC553415A4C61746ELLU, // tkr_Latn_AZ
+ 0xCD534E5044657661LLU, // tkt_Deva_NP
+ 0x746C50484C61746ELLU, // tl_Latn_PH
+ 0xE173415A4C61746ELLU, // tly_Latn_AZ
+ 0x9D934E454C61746ELLU, // tmh_Latn_NE
+ 0x746E5A414C61746ELLU, // tn_Latn_ZA
+ 0x746F544F4C61746ELLU, // to_Latn_TO
+ 0x99D34D574C61746ELLU, // tog_Latn_MW
+ 0xA1F350474C61746ELLU, // tpi_Latn_PG
+ 0x747254524C61746ELLU, // tr_Latn_TR
+ 0xD23354524C61746ELLU, // tru_Latn_TR
+ 0xD63354574C61746ELLU, // trv_Latn_TW
+ 0x74735A414C61746ELLU, // ts_Latn_ZA
+ 0x8E5347524772656BLLU, // tsd_Grek_GR
+ 0x96534E5044657661LLU, // tsf_Deva_NP
+ 0x9A5350484C61746ELLU, // tsg_Latn_PH
+ 0xA653425454696274LLU, // tsj_Tibt_BT
+ 0x747452554379726CLLU, // tt_Cyrl_RU
+ 0xA67355474C61746ELLU, // ttj_Latn_UG
+ 0xCA73544854686169LLU, // tts_Thai_TH
+ 0xCE73415A4C61746ELLU, // ttt_Latn_AZ
+ 0xB2934D574C61746ELLU, // tum_Latn_MW
+ 0xAEB354564C61746ELLU, // tvl_Latn_TV
+ 0xC2D34E454C61746ELLU, // twq_Latn_NE
+ 0x9AF3434E54616E67LLU, // txg_Tang_CN
+ 0x747950464C61746ELLU, // ty_Latn_PF
+ 0xD71352554379726CLLU, // tyv_Cyrl_RU
+ 0xB3334D414C61746ELLU, // tzm_Latn_MA
+ 0xB07452554379726CLLU, // udm_Cyrl_RU
+ 0x7567434E41726162LLU, // ug_Arab_CN
+ 0x75674B5A4379726CLLU, // ug_Cyrl_KZ
+ 0x80D4535955676172LLU, // uga_Ugar_SY
+ 0x756B55414379726CLLU, // uk_Cyrl_UA
+ 0xA174464D4C61746ELLU, // uli_Latn_FM
+ 0x8594414F4C61746ELLU, // umb_Latn_AO
+ 0xC5B4494E42656E67LLU, // unr_Beng_IN
+ 0xC5B44E5044657661LLU, // unr_Deva_NP
+ 0xDDB4494E42656E67LLU, // unx_Beng_IN
+ 0x7572504B41726162LLU, // ur_Arab_PK
+ 0x757A414641726162LLU, // uz_Arab_AF
+ 0x757A555A4C61746ELLU, // uz_Latn_UZ
+ 0xA0154C5256616969LLU, // vai_Vaii_LR
+ 0x76655A414C61746ELLU, // ve_Latn_ZA
+ 0x889549544C61746ELLU, // vec_Latn_IT
+ 0xBC9552554C61746ELLU, // vep_Latn_RU
+ 0x7669564E4C61746ELLU, // vi_Latn_VN
+ 0x891553584C61746ELLU, // vic_Latn_SX
+ 0xC97542454C61746ELLU, // vls_Latn_BE
+ 0x959544454C61746ELLU, // vmf_Latn_DE
+ 0xD9954D5A4C61746ELLU, // vmw_Latn_MZ
+ 0xCDD552554C61746ELLU, // vot_Latn_RU
+ 0xBA3545454C61746ELLU, // vro_Latn_EE
+ 0xB695545A4C61746ELLU, // vun_Latn_TZ
+ 0x776142454C61746ELLU, // wa_Latn_BE
+ 0x901643484C61746ELLU, // wae_Latn_CH
+ 0xAC16455445746869LLU, // wal_Ethi_ET
+ 0xC41650484C61746ELLU, // war_Latn_PH
+ 0xBC3641554C61746ELLU, // wbp_Latn_AU
+ 0xC036494E54656C75LLU, // wbq_Telu_IN
+ 0xC436494E44657661LLU, // wbr_Deva_IN
+ 0xC97657464C61746ELLU, // wls_Latn_WF
+ 0xA1B64B4D41726162LLU, // wni_Arab_KM
+ 0x776F534E4C61746ELLU, // wo_Latn_SN
+ 0xB276494E44657661LLU, // wtm_Deva_IN
+ 0xD296434E48616E73LLU, // wuu_Hans_CN
+ 0xD41742524C61746ELLU, // xav_Latn_BR
+ 0xC457545243617269LLU, // xcr_Cari_TR
+ 0x78685A414C61746ELLU, // xh_Latn_ZA
+ 0x897754524C796369LLU, // xlc_Lyci_TR
+ 0x8D7754524C796469LLU, // xld_Lydi_TR
+ 0x9597474547656F72LLU, // xmf_Geor_GE
+ 0xB597434E4D616E69LLU, // xmn_Mani_CN
+ 0xC59753444D657263LLU, // xmr_Merc_SD
+ 0x81B753414E617262LLU, // xna_Narb_SA
+ 0xC5B7494E44657661LLU, // xnr_Deva_IN
+ 0x99D755474C61746ELLU, // xog_Latn_UG
+ 0xC5F7495250727469LLU, // xpr_Prti_IR
+ 0x8257594553617262LLU, // xsa_Sarb_YE
+ 0xC6574E5044657661LLU, // xsr_Deva_NP
+ 0xB8184D5A4C61746ELLU, // yao_Latn_MZ
+ 0xBC18464D4C61746ELLU, // yap_Latn_FM
+ 0xD418434D4C61746ELLU, // yav_Latn_CM
+ 0x8438434D4C61746ELLU, // ybb_Latn_CM
+ 0x796F4E474C61746ELLU, // yo_Latn_NG
+ 0xAE3842524C61746ELLU, // yrl_Latn_BR
+ 0x82984D584C61746ELLU, // yua_Latn_MX
+ 0x9298434E48616E73LLU, // yue_Hans_CN
+ 0x9298484B48616E74LLU, // yue_Hant_HK
+ 0x7A61434E4C61746ELLU, // za_Latn_CN
+ 0x981953444C61746ELLU, // zag_Latn_SD
+ 0xA4794B4D41726162LLU, // zdj_Arab_KM
+ 0x80994E4C4C61746ELLU, // zea_Latn_NL
+ 0x9CD94D4154666E67LLU, // zgh_Tfng_MA
+ 0x7A685457426F706FLLU, // zh_Bopo_TW
+ 0x7A68545748616E62LLU, // zh_Hanb_TW
+ 0x7A68434E48616E73LLU, // zh_Hans_CN
+ 0x7A68545748616E74LLU, // zh_Hant_TW
+ 0xB17954474C61746ELLU, // zlm_Latn_TG
+ 0xA1994D594C61746ELLU, // zmi_Latn_MY
+ 0x7A755A414C61746ELLU, // zu_Latn_ZA
+ 0x833954524C61746ELLU, // zza_Latn_TR
});
const std::unordered_map<uint32_t, uint32_t> ARAB_PARENTS({
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 2abb3d5179a0..4be8bd9a863e 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -831,6 +831,11 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
createBuffers(surface, surfaceFormat, extent);
+ // The window content is not updated (frozen) until a buffer of the window size is received.
+ // This prevents temporary stretching of the window after it is resized, but before the first
+ // buffer with new size is enqueued.
+ native_window_set_scaling_mode(surface->mNativeWindow, NATIVE_WINDOW_SCALING_MODE_FREEZE);
+
return true;
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index f1d9397783ed..89d3cc4f5083 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,7 +14,6 @@
cc_library_shared {
name: "libinputservice",
-
srcs: [
"PointerController.cpp",
"SpriteController.cpp",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0a90f85cda0e..80d8e72a87e2 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,10 +89,6 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
mLocked.animationPending = false;
- mLocked.displayWidth = -1;
- mLocked.displayHeight = -1;
- mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
-
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
@@ -110,15 +106,6 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
mLocked.lastFrameUpdatedTime = 0;
mLocked.buttonState = 0;
-
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-
- loadResources();
-
- if (mLocked.pointerIcon.isValid()) {
- mLocked.pointerIconChanged = true;
- updatePointerLocked();
- }
}
PointerController::~PointerController() {
@@ -144,23 +131,15 @@ bool PointerController::getBounds(float* outMinX, float* outMinY,
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
- if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+
+ if (!mLocked.viewport.isValid()) {
return false;
}
- *outMinX = 0;
- *outMinY = 0;
- switch (mLocked.displayOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
- *outMaxX = mLocked.displayHeight - 1;
- *outMaxY = mLocked.displayWidth - 1;
- break;
- default:
- *outMaxX = mLocked.displayWidth - 1;
- *outMaxY = mLocked.displayHeight - 1;
- break;
- }
+ *outMinX = mLocked.viewport.logicalLeft;
+ *outMinY = mLocked.viewport.logicalTop;
+ *outMaxX = mLocked.viewport.logicalRight - 1;
+ *outMaxY = mLocked.viewport.logicalBottom - 1;
return true;
}
@@ -231,6 +210,12 @@ void PointerController::getPosition(float* outX, float* outY) const {
*outY = mLocked.pointerY;
}
+int32_t PointerController::getDisplayId() const {
+ AutoMutex _l(mLock);
+
+ return mLocked.viewport.displayId;
+}
+
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
@@ -355,48 +340,57 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout
void PointerController::reloadPointerResources() {
AutoMutex _l(mLock);
- loadResources();
+ loadResourcesLocked();
+ updatePointerLocked();
+}
- if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources);
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+ if (viewport.orientation == DISPLAY_ORIENTATION_90
+ || viewport.orientation == DISPLAY_ORIENTATION_270) {
+ width = viewport.deviceHeight;
+ height = viewport.deviceWidth;
+ } else {
+ width = viewport.deviceWidth;
+ height = viewport.deviceHeight;
}
-
- mLocked.presentationChanged = true;
- updatePointerLocked();
}
-void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
+void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
AutoMutex _l(mLock);
-
- // Adjust to use the display's unrotated coordinate frame.
- if (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270) {
- int32_t temp = height;
- height = width;
- width = temp;
+ if (viewport == mLocked.viewport) {
+ return;
}
- if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
- mLocked.displayWidth = width;
- mLocked.displayHeight = height;
+ const DisplayViewport oldViewport = mLocked.viewport;
+ mLocked.viewport = viewport;
+
+ int32_t oldDisplayWidth, oldDisplayHeight;
+ getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+ int32_t newDisplayWidth, newDisplayHeight;
+ getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+ // Reset cursor position to center if size or display changed.
+ if (oldViewport.displayId != viewport.displayId
+ || oldDisplayWidth != newDisplayWidth
+ || oldDisplayHeight != newDisplayHeight) {
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
+ // Reload icon resources for density may be changed.
+ loadResourcesLocked();
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
- }
-
- if (mLocked.displayOrientation != orientation) {
+ } else if (oldViewport.orientation != viewport.orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
@@ -405,37 +399,37 @@ void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_
float temp;
// Undo the previous rotation.
- switch (mLocked.displayOrientation) {
+ switch (oldViewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
- x = mLocked.displayWidth - y;
+ x = oldViewport.deviceHeight - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = oldViewport.deviceWidth - x;
+ y = oldViewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
- y = mLocked.displayHeight - temp;
+ y = oldViewport.deviceWidth - temp;
break;
}
// Perform the new rotation.
- switch (orientation) {
+ switch (viewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = mLocked.displayWidth - temp;
+ y = viewport.deviceHeight - temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - x;
+ y = viewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
- x = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - y;
y = temp;
break;
}
@@ -444,7 +438,6 @@ void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
- mLocked.displayOrientation = orientation;
}
updatePointerLocked();
@@ -614,11 +607,16 @@ void PointerController::removeInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
-void PointerController::updatePointerLocked() {
+void PointerController::updatePointerLocked() REQUIRES(mLock) {
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
+
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+ mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -729,8 +727,18 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() {
}
}
-void PointerController::loadResources() {
+void PointerController::loadResourcesLocked() REQUIRES(mLock) {
mPolicy->loadPointerResources(&mResources);
+
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
+ }
+
+ mLocked.pointerIconChanged = true;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 7f4e5a59c9b6..a32cc42a3342 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,6 +23,7 @@
#include <vector>
#include <ui/DisplayInfo.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
@@ -96,6 +97,7 @@ public:
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
+ virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
@@ -106,7 +108,7 @@ public:
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
+ void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
@@ -156,9 +158,7 @@ private:
size_t animationFrameIndex;
nsecs_t lastFrameUpdatedTime;
- int32_t displayWidth;
- int32_t displayHeight;
- int32_t displayOrientation;
+ DisplayViewport viewport;
InactivityTimeout inactivityTimeout;
@@ -182,7 +182,7 @@ private:
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
- } mLocked;
+ } mLocked GUARDED_BY(mLock);
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@ private:
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
- void loadResources();
+ void loadResourcesLocked();
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index eb2bc98ec9e9..c1868d3a94d6 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,13 +144,16 @@ void SpriteController::doUpdateSprites() {
}
}
- // Resize sprites if needed.
+ // Resize and/or reparent sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
+ if (update.state.surfaceControl == nullptr) {
+ continue;
+ }
- if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+ if (update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
@@ -170,6 +173,12 @@ void SpriteController::doUpdateSprites() {
}
}
}
+
+ // If surface is a new one, we have to set right layer stack.
+ if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+ t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+ needApplyTransaction = true;
+ }
}
if (needApplyTransaction) {
t.apply();
@@ -236,7 +245,7 @@ void SpriteController::doUpdateSprites() {
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -445,6 +454,15 @@ void SpriteController::SpriteImpl::setTransformationMatrix(
}
}
+void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.displayId != displayId) {
+ mLocked.state.displayId = displayId;
+ invalidateLocked(DIRTY_DISPLAY_ID);
+ }
+}
+
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 31e43e9b99e5..5b216f50d113 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,6 +125,9 @@ public:
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+ /* Sets the id of the display where the sprite should be shown. */
+ virtual void setDisplayId(int32_t displayId) = 0;
};
/*
@@ -170,6 +173,7 @@ private:
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
+ DIRTY_DISPLAY_ID = 1 << 7,
};
/* Describes the state of a sprite.
@@ -180,7 +184,7 @@ private:
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
- positionX(0), positionY(0), layer(0), alpha(1.0f),
+ positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
@@ -193,6 +197,7 @@ private:
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
+ int32_t displayId;
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth;
@@ -225,6 +230,7 @@ private:
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+ virtual void setDisplayId(int32_t displayId);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ae87998a1615..32c752064a69 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -96,9 +96,7 @@ interface ILocationManager
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
void setTestProviderLocation(String provider, in Location loc, String opPackageName);
- void clearTestProviderLocation(String provider, String opPackageName);
void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
- void clearTestProviderEnabled(String provider, String opPackageName);
// --- deprecated ---
void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
@@ -108,12 +106,8 @@ interface ILocationManager
// --- internal ---
- // Used by location providers to tell the location manager when it has a new location.
- // Passive is true if the location is coming from the passive provider, in which case
- // it need not be shared with other providers.
+ // --- deprecated ---
void reportLocation(in Location location, boolean passive);
-
- // Used when a (initially Gnss) Location batch arrives
void reportLocationBatch(in List<Location> locations);
// for reporting callback completion
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3bf98b352b40..334170e5ce03 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1537,14 +1537,11 @@ public class LocationManager {
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This function has always been a no-op, and may be removed in the future.
*/
- public void clearTestProviderLocation(String provider) {
- try {
- mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ @Deprecated
+ public void clearTestProviderLocation(String provider) {}
/**
* Sets a mock enabled value for the given provider. This value will be used in place
@@ -1575,13 +1572,12 @@ public class LocationManager {
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
*/
+ @Deprecated
public void clearTestProviderEnabled(String provider) {
- try {
- mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setTestProviderEnabled(provider, true);
}
/**
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 39c2d92bf278..71b54fb65ae5 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -16,29 +16,26 @@
package com.android.internal.location;
-import android.location.Location;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.WorkSource;
-import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderRequest;
/**
- * Binder interface for services that implement location providers.
- * <p>Use {@link LocationProviderBase} as a helper to implement this
- * interface.
+ * Binder interface for services that implement location providers. Do not implement this directly,
+ * extend {@link LocationProviderBase} instead.
* @hide
*/
interface ILocationProvider {
- void enable();
- void disable();
- void setRequest(in ProviderRequest request, in WorkSource ws);
+ oneway void setLocationProviderManager(in ILocationProviderManager manager);
- // --- deprecated (but still supported) ---
- ProviderProperties getProperties();
+ oneway void setRequest(in ProviderRequest request, in WorkSource ws);
+
+ oneway void sendExtraCommand(String command, in Bundle extras);
+
+ // --- deprecated and will be removed the future ---
int getStatus(out Bundle extras);
long getStatusUpdateTime();
- boolean sendExtraCommand(String command, inout Bundle extras);
}
diff --git a/location/java/android/location/IGnssStatusProvider.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
index 006b5d3c0c20..b1b8f0c7c3f7 100644
--- a/location/java/android/location/IGnssStatusProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-package android.location;
+package com.android.internal.location;
-import android.location.IGnssStatusListener;
+import android.location.Location;
+
+import com.android.internal.location.ProviderProperties;
/**
- * An interface for location providers that provide GNSS status information.
- *
- * {@hide}
+ * Binder interface for manager of all location providers.
+ * @hide
*/
-interface IGnssStatusProvider {
- void registerGnssStatusCallback(IGnssStatusListener callback);
- void unregisterGnssStatusCallback(IGnssStatusListener callback);
+interface ILocationProviderManager {
+
+ void onSetEnabled(boolean enabled);
+
+ void onSetProperties(in ProviderProperties properties);
+
+ void onReportLocation(in Location location);
}
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 88919f628638..a45c20d9d09d 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,15 +16,15 @@
package com.android.internal.location;
-import java.util.ArrayList;
-import java.util.List;
-
import android.annotation.UnsupportedAppUsage;
import android.location.LocationRequest;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.TimeUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/** @hide */
public final class ProviderRequest implements Parcelable {
/** Location reporting is requested (true) */
@@ -36,6 +36,13 @@ public final class ProviderRequest implements Parcelable {
public long interval = Long.MAX_VALUE;
/**
+ * When this flag is true, providers should ignore all location settings, user consents, power
+ * restrictions or any other restricting factors and always satisfy this request to the best of
+ * their ability. This flag should only be used in event of an emergency.
+ */
+ public boolean forceLocation = false;
+
+ /**
* Whether provider shall make stronger than normal tradeoffs to substantially restrict power
* use.
*/
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index d19559e8cccd..10c344775019 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -8,14 +8,18 @@ package com.android.location.provider {
public abstract class LocationProviderBase {
ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
method public android.os.IBinder getBinder();
- method public abstract void onDisable();
- method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public abstract void onEnable();
- method public deprecated int onGetStatus(android.os.Bundle);
- method public deprecated long onGetStatusUpdateTime();
- method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
- method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
- method public final void reportLocation(android.location.Location);
+ method public boolean isEnabled();
+ method protected deprecated void onDisable();
+ method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method protected deprecated void onEnable();
+ method protected deprecated int onGetStatus(android.os.Bundle);
+ method protected deprecated long onGetStatusUpdateTime();
+ method protected void onInit();
+ method protected boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+ method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+ method public void reportLocation(android.location.Location);
+ method public void setEnabled(boolean);
+ method public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
field public static final java.lang.String FUSED_PROVIDER = "fused";
}
@@ -38,6 +42,7 @@ package com.android.location.provider {
}
public final class ProviderRequestUnbundled {
+ method public boolean getForceLocation();
method public long getInterval();
method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
method public boolean getReportLocation();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index d45a4bac8f96..5bcec92c4fba 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,6 +16,7 @@
package com.android.location.provider;
+import android.annotation.Nullable;
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
@@ -29,12 +30,11 @@ import android.os.WorkSource;
import android.util.Log;
import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.PrintWriter;
/**
@@ -55,12 +55,6 @@ import java.io.PrintWriter;
* of this package for more information.
*/
public abstract class LocationProviderBase {
- private final String TAG;
-
- /** @hide */
- protected final ILocationManager mLocationManager;
- private final ProviderProperties mProperties;
- private final IBinder mBinder;
/**
* Bundle key for a version of the location containing no GPS data.
@@ -77,49 +71,34 @@ public abstract class LocationProviderBase {
*/
public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
- private final class Service extends ILocationProvider.Stub {
- @Override
- public void enable() {
- onEnable();
- }
- @Override
- public void disable() {
- onDisable();
- }
- @Override
- public void setRequest(ProviderRequest request, WorkSource ws) {
- onSetRequest(new ProviderRequestUnbundled(request), ws);
- }
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
- @Override
- public int getStatus(Bundle extras) {
- return onGetStatus(extras);
- }
- @Override
- public long getStatusUpdateTime() {
- return onGetStatusUpdateTime();
- }
- @Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return onSendExtraCommand(command, extras);
- }
- @Override
- public void dump(FileDescriptor fd, String[] args) {
- PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
- onDump(fd, pw, args);
- pw.flush();
- }
- }
+ private final String mTag;
+ private final IBinder mBinder;
+
+ /**
+ * This field may be removed in the future, do not rely on it.
+ *
+ * @deprecated Do not use this field! Use LocationManager APIs instead. If you use this field
+ * you may be broken in the future.
+ * @hide
+ */
+ @Deprecated
+ protected final ILocationManager mLocationManager;
+
+ // 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;
public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
- TAG = tag;
- IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
- mLocationManager = ILocationManager.Stub.asInterface(b);
- mProperties = properties.getProviderProperties();
+ mTag = tag;
mBinder = new Service();
+
+ mLocationManager = ILocationManager.Stub.asInterface(
+ ServiceManager.getService(Context.LOCATION_SERVICE));
+
+ mManager = null;
+ mProperties = properties.getProviderProperties();
+ mEnabled = true;
}
public IBinder getBinder() {
@@ -127,51 +106,116 @@ public abstract class LocationProviderBase {
}
/**
- * Used by the location provider to report new locations.
+ * Sets whether this provider is currently enabled 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
+ * whether to attempt to satisfy those requests or not.
*
- * @param location new Location to report
- *
- * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+ * 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.
*/
- public final void reportLocation(Location location) {
- try {
- mLocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException", e);
- } catch (Exception e) {
- // never crash provider, might be running in a system process
- Log.e(TAG, "Exception", e);
+ public void setEnabled(boolean enabled) {
+ synchronized (mBinder) {
+ if (mEnabled == enabled) {
+ return;
+ }
+
+ mEnabled = enabled;
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetEnabled(mEnabled);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
}
}
/**
- * Enable the location provider.
- * <p>The provider may initialize resources, but does
- * not yet need to report locations.
+ * Sets the provider properties that may be queried by clients. Generally speaking, providers
+ * should try to avoid changing their properties after construction.
*/
- public abstract void onEnable();
+ public void setProperties(ProviderPropertiesUnbundled properties) {
+ synchronized (mBinder) {
+ mProperties = properties.getProviderProperties();
+ }
+
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onSetProperties(mProperties);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
/**
- * Disable the location provider.
- * <p>The provider must release resources, and stop
- * performing work. It may no longer report locations.
+ * Returns true if this provider has been set as enabled. This will be true unless explicitly
+ * set otherwise.
*/
- public abstract void onDisable();
+ public boolean isEnabled() {
+ return mEnabled;
+ }
/**
- * Set the {@link ProviderRequest} requirements for this provider.
- * <p>Each call to this method overrides all previous requests.
- * <p>This method might trigger the provider to start returning
- * locations, or to stop returning locations, depending on the
- * parameters in the request.
+ * Reports a new location from this provider.
*/
- public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
+ public void reportLocation(Location location) {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocation(location);
+ } catch (RemoteException | RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
+ }
+
+ protected void onInit() {
+ // call once so that providers designed for APIs pre-Q are not broken
+ onEnable();
+ }
+
+ /**
+ * @deprecated This callback will be invoked once when the provider is created to maintain
+ * backwards compatibility with providers not designed for Android Q and above. This method
+ * should only be implemented in location providers that need to support SDKs below Android Q.
+ * Even in this case, it is usually unnecessary to implement this callback with the correct
+ * design. This method may be removed in the future.
+ */
+ @Deprecated
+ protected void onEnable() {}
+
+ /**
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. Even in
+ * this case, it is usually unnecessary to implement this callback with the correct design. This
+ * method may be removed in the future.
+ */
+ @Deprecated
+ protected void onDisable() {}
+
+ /**
+ * Set the {@link ProviderRequest} requirements for this provider. Each call to this method
+ * overrides all previous requests. This method might trigger the provider to start returning
+ * locations, or to stop returning locations, depending on the parameters in the request.
+ */
+ protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
/**
* Dump debug information.
*/
- public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- }
+ protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
/**
* This method will no longer be invoked.
@@ -187,10 +231,12 @@ public abstract class LocationProviderBase {
* <p>If extras is non-null, additional status information may be
* added to it in the form of provider-specific key/value pairs.
*
- * @deprecated This method will no longer be invoked.
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. This
+ * method may be removed in the future.
*/
@Deprecated
- public int onGetStatus(Bundle extras) {
+ protected int onGetStatus(Bundle extras) {
return LocationProvider.AVAILABLE;
}
@@ -206,24 +252,64 @@ public abstract class LocationProviderBase {
*
* @return time of last status update in millis since last reboot
*
- * @deprecated This method will no longer be invoked.
+ * @deprecated This callback will be never be invoked on Android Q and above. This method should
+ * only be implemented in location providers that need to support SDKs below Android Q. This
+ * method may be removed in the future.
*/
@Deprecated
- public long onGetStatusUpdateTime() {
+ protected long onGetStatusUpdateTime() {
return 0;
}
/**
- * Implements addditional location provider specific additional commands.
- *
- * @param command name of the command to send to the provider.
- * @param extras optional arguments for the command (or null).
- * The provider may optionally fill the extras Bundle with results from the command.
- *
- * @return true if the command succeeds.
+ * Implements location provider specific custom commands. The return value will be ignored on
+ * Android Q and above.
*/
- public boolean onSendExtraCommand(String command, Bundle extras) {
- // default implementation
+ protected boolean onSendExtraCommand(@Nullable String command, @Nullable Bundle extras) {
return false;
}
+
+ private final class Service extends ILocationProvider.Stub {
+
+ @Override
+ public void setLocationProviderManager(ILocationProviderManager manager) {
+ synchronized (mBinder) {
+ try {
+ manager.onSetProperties(mProperties);
+ manager.onSetEnabled(mEnabled);
+ } catch (RemoteException e) {
+ Log.w(mTag, e);
+ }
+
+ mManager = manager;
+ }
+
+ onInit();
+ }
+
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource ws) {
+ onSetRequest(new ProviderRequestUnbundled(request), ws);
+ }
+
+ @Override
+ public int getStatus(Bundle extras) {
+ return onGetStatus(extras);
+ }
+
+ @Override
+ public long getStatusUpdateTime() {
+ return onGetStatusUpdateTime();
+ }
+
+ @Override
+ public void sendExtraCommand(String command, Bundle extras) {
+ onSendExtraCommand(command, extras);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ onDump(fd, pw, args);
+ }
+ }
}
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6a8e61877e46..b825b58cd3e9 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -16,13 +16,13 @@
package com.android.location.provider;
-import java.util.ArrayList;
-import java.util.List;
-
import android.location.LocationRequest;
import com.android.internal.location.ProviderRequest;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class is an interface to Provider Requests for unbundled applications.
*
@@ -46,6 +46,10 @@ public final class ProviderRequestUnbundled {
return mRequest.interval;
}
+ public boolean getForceLocation() {
+ return mRequest.forceLocation;
+ }
+
/**
* Never null.
*/
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 24d27256ca03..793aa270e2fd 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -213,6 +213,8 @@ import java.util.Objects;
* (e.g.{@link AudioTrack#getPlaybackHeadPosition()
* AudioTrack.getPlaybackHeadPosition()}),
* depending on the context where audio frame is used.
+ * For the purposes of {@link AudioFormat#getFrameSizeInBytes()}, a compressed data format
+ * returns a frame size of 1 byte.
*/
public final class AudioFormat implements Parcelable {
@@ -707,6 +709,16 @@ public final class AudioFormat implements Parcelable {
channelCount = 0; // position and index channel count mismatch
}
mChannelCount = channelCount;
+
+ int frameSizeInBytes = 1;
+ try {
+ frameSizeInBytes = getBytesPerSample(mEncoding) * channelCount;
+ } catch (IllegalArgumentException iae) {
+ // ignored
+ }
+ // it is possible that channel count is 0, so ensure we return 1 for
+ // mFrameSizeInBytes for consistency.
+ mFrameSizeInBytes = frameSizeInBytes != 0 ? frameSizeInBytes : 1;
}
/** @hide */
@@ -734,6 +746,7 @@ public final class AudioFormat implements Parcelable {
// Derived values computed in the constructor, cached here.
private final int mChannelCount;
+ private final int mFrameSizeInBytes;
/**
* Return the encoding.
@@ -788,6 +801,25 @@ public final class AudioFormat implements Parcelable {
return mChannelCount;
}
+ /**
+ * Return the frame size in bytes.
+ *
+ * For PCM or PCM packed compressed data this is the size of a sample multiplied
+ * by the channel count. For all other cases, including invalid/unset channel masks,
+ * this will return 1 byte.
+ * As an example, a stereo 16-bit PCM format would have a frame size of 4 bytes,
+ * an 8 channel float PCM format would have a frame size of 32 bytes,
+ * and a compressed data format (not packed in PCM) would have a frame size of 1 byte.
+ *
+ * Both {@link AudioRecord} or {@link AudioTrack} process data in multiples of
+ * this frame size.
+ *
+ * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
+ */
+ public int getFrameSizeInBytes() {
+ return mFrameSizeInBytes;
+ }
+
/** @hide */
public int getPropertySetMask() {
return mPropertySetMask;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d10900e5d160..da52a268e384 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5152,6 +5152,15 @@ public class AudioManager {
return reportedSurroundFormats;
}
+ /**
+ * Return if audio haptic coupled playback is supported or not.
+ *
+ * @return whether audio haptic playback supported.
+ */
+ public static boolean isHapticPlaybackSupported() {
+ return AudioSystem.isHapticPlaybackSupported();
+ }
+
//---------------------------------------------------------
// Inner classes
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 082a375470ee..36f635a8e572 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -933,6 +933,11 @@ public class AudioSystem
*/
public static native int setA11yServicesUids(int[] uids);
+ /**
+ * @see AudioManager#isHapticPlaybackSupported()
+ */
+ public static native boolean isHapticPlaybackSupported();
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e4d47952b8c9..5e77fdf0a121 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -995,6 +995,35 @@ public class AudioTrack extends PlayerBase
}
}
+ /**
+ * Returns whether direct playback of an audio format with the provided attributes is
+ * currently supported on the system.
+ * <p>Direct playback means that the audio stream is not resampled or downmixed
+ * by the framework. Checking for direct support can help the app select the representation
+ * of audio content that most closely matches the capabilities of the device and peripherials
+ * (e.g. A/V receiver) connected to it. Note that the provided stream can still be re-encoded
+ * or mixed with other streams, if needed.
+ * <p>Also note that this query only provides information about the support of an audio format.
+ * It does not indicate whether the resources necessary for the playback are available
+ * at that instant.
+ * @param format a non-null {@link AudioFormat} instance describing the format of
+ * the audio data.
+ * @param attributes a non-null {@link AudioAttributes} instance.
+ * @return true if the given audio format can be played directly.
+ */
+ public static boolean isDirectPlaybackSupported(@NonNull AudioFormat format,
+ @NonNull AudioAttributes attributes) {
+ if (format == null) {
+ throw new IllegalArgumentException("Illegal null AudioFormat argument");
+ }
+ if (attributes == null) {
+ throw new IllegalArgumentException("Illegal null AudioAttributes argument");
+ }
+ return native_is_direct_output_supported(format.getEncoding(), format.getSampleRate(),
+ format.getChannelMask(), format.getChannelIndexMask(),
+ attributes.getContentType(), attributes.getUsage(), attributes.getFlags());
+ }
+
// mask of all the positional channels supported, however the allowed combinations
// are further restricted by the matching left/right rule and
// AudioSystem.OUT_CHANNEL_COUNT_MAX
@@ -3328,6 +3357,9 @@ public class AudioTrack extends PlayerBase
// Native methods called from the Java side
//--------------------
+ private static native boolean native_is_direct_output_supported(int encoding, int sampleRate,
+ int channelMask, int channelIndexMask, int contentType, int usage, int flags);
+
// post-condition: mStreamType is overwritten with a value
// that reflects the audio attributes (e.g. an AudioAttributes object with a usage of
// AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1d27c03e1dcb..242ae46f4d33 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3308,6 +3308,55 @@ final public class MediaCodec {
public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
/**
+ * Set the HDR10+ metadata on the next queued input frame.
+ *
+ * Provide a byte array of data that's conforming to the
+ * user_data_registered_itu_t_t35() syntax of SEI message for ST 2094-40.
+ *<p>
+ * For decoders:
+ *<p>
+ * When a decoder is configured for one of the HDR10+ profiles that uses
+ * out-of-band metadata (such as {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus} or {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}), this
+ * parameter sets the HDR10+ metadata on the next input buffer queued
+ * to the decoder. A decoder supporting these profiles must propagate
+ * the metadata to the format of the output buffer corresponding to this
+ * particular input buffer (under key {@link MediaFormat#KEY_HDR10_PLUS_INFO}).
+ * The metadata should be applied to that output buffer and the buffers
+ * following it (in display order), until the next output buffer (in
+ * display order) upon which an HDR10+ metadata is set.
+ *<p>
+ * This parameter shouldn't be set if the decoder is not configured for
+ * an HDR10+ profile that uses out-of-band metadata. In particular,
+ * it shouldn't be set for HDR10+ profiles that uses in-band metadata
+ * where the metadata is embedded in the input buffers, for example
+ * {@link MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}.
+ *<p>
+ * For encoders:
+ *<p>
+ * When an encoder is configured for one of the HDR10+ profiles and the
+ * operates in byte buffer input mode (instead of surface input mode),
+ * this parameter sets the HDR10+ metadata on the next input buffer queued
+ * to the encoder. For the HDR10+ profiles that uses out-of-band metadata
+ * (such as {@link MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus},
+ * or {@link MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}),
+ * the metadata must be propagated to the format of the output buffer
+ * corresponding to this particular input buffer (under key {@link
+ * MediaFormat#KEY_HDR10_PLUS_INFO}). For the HDR10+ profiles that uses
+ * in-band metadata (such as {@link
+ * MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}), the
+ * metadata info must be embedded in the corresponding output buffer itself.
+ *<p>
+ * This parameter shouldn't be set if the encoder is not configured for
+ * an HDR10+ profile, or if it's operating in surface input mode.
+ *<p>
+ *
+ * @see MediaFormat#KEY_HDR10_PLUS_INFO
+ */
+ public static final String PARAMETER_KEY_HDR10_PLUS_INFO = MediaFormat.KEY_HDR10_PLUS_INFO;
+
+ /**
* Communicate additional parameter changes to the component instance.
* <b>Note:</b> Some of these parameter changes may silently fail to apply.
*
@@ -3325,7 +3374,14 @@ final public class MediaCodec {
int i = 0;
for (final String key: params.keySet()) {
keys[i] = key;
- values[i] = params.get(key);
+ Object value = params.get(key);
+
+ // Bundle's byte array is a byte[], JNI layer only takes ByteBuffer
+ if (value instanceof byte[]) {
+ values[i] = ByteBuffer.wrap((byte[])value);
+ } else {
+ values[i] = value;
+ }
++i;
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 6301993a74be..902582905b0e 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1135,6 +1135,10 @@ public final class MediaCodecInfo {
maxChannels = 6;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC)) {
+ sampleRates = new int[] { 48000 };
+ bitRates = Range.create(32000, 6144000);
+ maxChannels = 16;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
sampleRates = new int[] { 44100, 48000, 96000, 192000 };
bitRates = Range.create(16000, 2688000);
@@ -2516,6 +2520,8 @@ public final class MediaCodecInfo {
case CodecProfileLevel.VP9Profile3:
case CodecProfileLevel.VP9Profile2HDR:
case CodecProfileLevel.VP9Profile3HDR:
+ case CodecProfileLevel.VP9Profile2HDR10Plus:
+ case CodecProfileLevel.VP9Profile3HDR10Plus:
break;
default:
Log.w(TAG, "Unrecognized profile "
@@ -2608,7 +2614,9 @@ public final class MediaCodecInfo {
switch (profileLevel.profile) {
case CodecProfileLevel.HEVCProfileMain:
case CodecProfileLevel.HEVCProfileMain10:
+ case CodecProfileLevel.HEVCProfileMainStill:
case CodecProfileLevel.HEVCProfileMain10HDR10:
+ case CodecProfileLevel.HEVCProfileMain10HDR10Plus:
break;
default:
Log.w(TAG, "Unrecognized profile "
@@ -2999,6 +3007,8 @@ public final class MediaCodecInfo {
// HDR profiles also support passing HDR metadata
public static final int VP9Profile2HDR = 0x1000;
public static final int VP9Profile3HDR = 0x2000;
+ public static final int VP9Profile2HDR10Plus = 0x4000;
+ public static final int VP9Profile3HDR10Plus = 0x8000;
// from OMX_VIDEO_VP9LEVELTYPE
public static final int VP9Level1 = 0x1;
@@ -3021,6 +3031,7 @@ public final class MediaCodecInfo {
public static final int HEVCProfileMain10 = 0x02;
public static final int HEVCProfileMainStill = 0x04;
public static final int HEVCProfileMain10HDR10 = 0x1000;
+ public static final int HEVCProfileMain10HDR10Plus = 0x2000;
// from OMX_VIDEO_HEVCLEVELTYPE
public static final int HEVCMainTierLevel1 = 0x1;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b7743c9db4c0..594a22457cc3 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -147,6 +147,7 @@ public final class MediaFormat {
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+ public static final String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
@@ -910,6 +911,27 @@ public final class MediaFormat {
public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
/**
+ * An optional key describing the HDR10+ metadata of the video content.
+ *
+ * The associated value is a ByteBuffer containing HDR10+ metadata conforming to the
+ * user_data_registered_itu_t_t35() syntax of SEI message for ST 2094-40. This key will
+ * be present on:
+ *<p>
+ * - The formats of output buffers of a decoder configured for HDR10+ profiles (such as
+ * {@link MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus}, {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus} or {@link
+ * MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}), or
+ *<p>
+ * - The formats of output buffers of an encoder configured for an HDR10+ profiles that
+ * uses out-of-band metadata (such as {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus} or {@link
+ * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}).
+ *
+ * @see MediaCodec#PARAMETER_KEY_HDR10_PLUS_INFO
+ */
+ public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
+
+ /**
* A key describing a unique ID for the content of a media track.
*
* <p>This key is used by {@link MediaExtractor}. Some extractors provide multiple encodings
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 29238d3b8ea4..f3442f4b4d33 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -500,17 +500,17 @@ static jlong android_media_MediaExtractor_getSampleTime(
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
int64_t sampleTimeUs;
status_t err = extractor->getSampleTime(&sampleTimeUs);
if (err == ERROR_END_OF_STREAM) {
- return -1ll;
+ return -1LL;
} else if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return -1ll;
+ return -1LL;
}
return (jlong) sampleTimeUs;
@@ -522,17 +522,17 @@ static jlong android_media_MediaExtractor_getSampleSize(
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
size_t sampleSize;
status_t err = extractor->getSampleSize(&sampleSize);
if (err == ERROR_END_OF_STREAM) {
- return -1ll;
+ return -1LL;
} else if (err != OK) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return -1ll;
+ return -1LL;
}
return (jlong) sampleSize;
@@ -858,13 +858,13 @@ static jlong android_media_MediaExtractor_getCachedDurationUs(
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return -1ll;
+ return -1LL;
}
int64_t cachedDurationUs;
bool eos;
if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
- return -1ll;
+ return -1LL;
}
return (jlong) cachedDurationUs;
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index ed2afdd84a7c..5f513207bd5f 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -513,7 +513,8 @@ Sample::~Sample()
static status_t decode(int fd, int64_t offset, int64_t length,
uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
- sp<MemoryHeapBase> heap, size_t *memsize) {
+ audio_channel_mask_t *channelMask, sp<MemoryHeapBase> heap,
+ size_t *memsize) {
ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
AMediaExtractor *ex = AMediaExtractor_new();
@@ -650,6 +651,10 @@ static status_t decode(int fd, int64_t offset, int64_t length,
(void)AMediaFormat_delete(format);
return UNKNOWN_ERROR;
}
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_MASK,
+ (int32_t*) channelMask)) {
+ *channelMask = AUDIO_CHANNEL_NONE;
+ }
(void)AMediaFormat_delete(format);
*memsize = written;
return OK;
@@ -665,12 +670,13 @@ status_t Sample::doLoad()
uint32_t sampleRate;
int numChannels;
audio_format_t format;
+ audio_channel_mask_t channelMask;
status_t status;
mHeap = new MemoryHeapBase(kDefaultHeapSize);
ALOGV("Start decode");
status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
- mHeap, &mSize);
+ &channelMask, mHeap, &mSize);
ALOGV("close(%d)", mFd);
::close(mFd);
mFd = -1;
@@ -697,6 +703,7 @@ status_t Sample::doLoad()
mSampleRate = sampleRate;
mNumChannels = numChannels;
mFormat = format;
+ mChannelMask = channelMask;
mState = READY;
return NO_ERROR;
@@ -781,7 +788,11 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV
// wrong audio audio buffer size (mAudioBufferSize)
unsigned long toggle = mToggle ^ 1;
void *userData = (void *)((unsigned long)this | toggle);
- audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels);
+ audio_channel_mask_t sampleChannelMask = sample->channelMask();
+ // When sample contains a not none channel mask, use it as is.
+ // Otherwise, use channel count to calculate channel mask.
+ audio_channel_mask_t channelMask = sampleChannelMask != AUDIO_CHANNEL_NONE
+ ? sampleChannelMask : audio_channel_out_mask_from_count(numChannels);
// do not create a new audio track if current track is compatible with sample parameters
#ifdef USE_SHARED_MEM_BUFFER
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 5c48a907c832..9d7410305c2c 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -58,6 +58,7 @@ public:
int numChannels() { return mNumChannels; }
int sampleRate() { return mSampleRate; }
audio_format_t format() { return mFormat; }
+ audio_channel_mask_t channelMask() { return mChannelMask; }
size_t size() { return mSize; }
int state() { return mState; }
uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
@@ -68,18 +69,19 @@ public:
private:
void init();
- size_t mSize;
- volatile int32_t mRefCount;
- uint16_t mSampleID;
- uint16_t mSampleRate;
- uint8_t mState;
- uint8_t mNumChannels;
- audio_format_t mFormat;
- int mFd;
- int64_t mOffset;
- int64_t mLength;
- sp<IMemory> mData;
- sp<MemoryHeapBase> mHeap;
+ size_t mSize;
+ volatile int32_t mRefCount;
+ uint16_t mSampleID;
+ uint16_t mSampleRate;
+ uint8_t mState;
+ uint8_t mNumChannels;
+ audio_format_t mFormat;
+ audio_channel_mask_t mChannelMask;
+ int mFd;
+ int64_t mOffset;
+ int64_t mLength;
+ sp<IMemory> mData;
+ sp<MemoryHeapBase> mHeap;
};
// stores pending events for stolen channels
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 617e49ac1120..a9a5450c536e 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -22,5 +22,6 @@
<string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
<string-array name="autofill_field_classification_available_algorithms">
<item>EDIT_DISTANCE</item>
+ <item>EXACT_MATCH</item>
</string-array>
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
index 9ba7e092f34b..e379db842b3b 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
@@ -15,8 +15,6 @@
*/
package android.ext.services.autofill;
-import static android.ext.services.autofill.EditDistanceScorer.DEFAULT_ALGORITHM;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -26,27 +24,72 @@ import android.view.autofill.AutofillValue;
import com.android.internal.util.ArrayUtils;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
private static final String TAG = "AutofillFieldClassificationServiceImpl";
+ private static final String DEFAULT_ALGORITHM = REQUIRED_ALGORITHM_EDIT_DISTANCE;
+
@Nullable
@Override
- public float[][] onGetScores(@Nullable String algorithmName,
- @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
- @NonNull List<String> userDataValues) {
+ /** @hide */
+ public float[][] onCalculateScores(@NonNull List<AutofillValue> actualValues,
+ @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable Map algorithms, @Nullable Map args) {
if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
- Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues ("
- + userDataValues + ")");
+ Log.w(TAG, "calculateScores(): empty currentvalues (" + actualValues
+ + ") or userValues (" + userDataValues + ")");
return null;
}
- if (algorithmName != null && !algorithmName.equals(DEFAULT_ALGORITHM)) {
- Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using "
- + DEFAULT_ALGORITHM + " instead");
- }
- return EditDistanceScorer.getScores(actualValues, userDataValues);
+ return calculateScores(actualValues, userDataValues, categoryIds, defaultAlgorithm,
+ defaultArgs, (HashMap<String, String>) algorithms,
+ (HashMap<String, Bundle>) args);
+ }
+
+ /** @hide */
+ public float[][] calculateScores(@NonNull List<AutofillValue> actualValues,
+ @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable HashMap<String, String> algorithms,
+ @Nullable HashMap<String, Bundle> args) {
+ final int actualValuesSize = actualValues.size();
+ final int userDataValuesSize = userDataValues.size();
+ final float[][] scores = new float[actualValuesSize][userDataValuesSize];
+
+ for (int j = 0; j < userDataValuesSize; j++) {
+ final String categoryId = categoryIds.get(j);
+ String algorithmName = defaultAlgorithm;
+ Bundle arg = defaultArgs;
+ if (algorithms != null && algorithms.containsKey(categoryId)) {
+ algorithmName = algorithms.get(categoryId);
+ }
+ if (args != null && args.containsKey(categoryId)) {
+ arg = args.get(categoryId);
+ }
+
+ if (algorithmName == null || (!algorithmName.equals(DEFAULT_ALGORITHM)
+ && !algorithmName.equals(REQUIRED_ALGORITHM_EXACT_MATCH))) {
+ Log.w(TAG, "algorithmName is " + algorithmName + ", defaulting to "
+ + DEFAULT_ALGORITHM);
+ algorithmName = DEFAULT_ALGORITHM;
+ }
+
+ for (int i = 0; i < actualValuesSize; i++) {
+ if (algorithmName.equals(DEFAULT_ALGORITHM)) {
+ scores[i][j] = EditDistanceScorer.calculateScore(actualValues.get(i),
+ userDataValues.get(j));
+ } else {
+ scores[i][j] = ExactMatch.calculateScore(actualValues.get(i),
+ userDataValues.get(j), arg);
+ }
+ }
+ }
+ return scores;
}
}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
index 302b16022c26..6a47901aa58e 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
@@ -17,13 +17,10 @@ package android.ext.services.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.Log;
import android.view.autofill.AutofillValue;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.List;
-
final class EditDistanceScorer {
private static final String TAG = "EditDistanceScorer";
@@ -31,15 +28,14 @@ final class EditDistanceScorer {
// TODO(b/70291841): STOPSHIP - set to false before launching
private static final boolean DEBUG = true;
- static final String DEFAULT_ALGORITHM = "EDIT_DISTANCE";
-
/**
* Gets the field classification score of 2 values based on the edit distance between them.
*
* <p>The score is defined as: @(max_length - edit_distance) / max_length
*/
@VisibleForTesting
- static float getScore(@Nullable AutofillValue actualValue, @Nullable String userDataValue) {
+ static float calculateScore(@Nullable AutofillValue actualValue,
+ @Nullable String userDataValue) {
if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
final String actualValueText = actualValue.getTextValue().toString();
@@ -123,26 +119,5 @@ final class EditDistanceScorer {
return d[m][n];
}
- /**
- * Gets the scores in a batch.
- */
- static float[][] getScores(@NonNull List<AutofillValue> actualValues,
- @NonNull List<String> userDataValues) {
- final int actualValuesSize = actualValues.size();
- final int userDataValuesSize = userDataValues.size();
- if (DEBUG) {
- Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
- + userDataValuesSize + " matrix for " + DEFAULT_ALGORITHM);
- }
- final float[][] scores = new float[actualValuesSize][userDataValuesSize];
-
- for (int i = 0; i < actualValuesSize; i++) {
- for (int j = 0; j < userDataValuesSize; j++) {
- final float score = getScore(actualValues.get(i), userDataValues.get(j));
- scores[i][j] = score;
- }
- }
- return scores;
- }
}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
new file mode 100644
index 000000000000..3e55c5c59e02
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.autofill;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+final class ExactMatch {
+
+ /**
+ * Gets the field classification score of 2 values based on whether they are an exact match
+ *
+ * @return {@code 1.0} if the two values are an exact match, {@code 0.0} otherwise.
+ */
+ @VisibleForTesting
+ static float calculateScore(@Nullable AutofillValue actualValue,
+ @Nullable String userDataValue, @Nullable Bundle args) {
+ if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
+
+ final String actualValueText = actualValue.getTextValue().toString();
+
+ final int suffixLength;
+ if (args != null) {
+ suffixLength = args.getInt("suffix", -1);
+
+ if (suffixLength < 0) {
+ throw new IllegalArgumentException("suffix argument is invalid");
+ }
+
+ final String actualValueSuffix;
+ if (suffixLength < actualValueText.length()) {
+ actualValueSuffix = actualValueText.substring(actualValueText.length()
+ - suffixLength);
+ } else {
+ actualValueSuffix = actualValueText;
+ }
+
+ final String userDataValueSuffix;
+ if (suffixLength < userDataValue.length()) {
+ userDataValueSuffix = userDataValue.substring(userDataValue.length()
+ - suffixLength);
+ } else {
+ userDataValueSuffix = userDataValue;
+ }
+
+ return (actualValueSuffix.equalsIgnoreCase(userDataValueSuffix)) ? 1 : 0;
+ } else {
+ return actualValueText.equalsIgnoreCase(userDataValue) ? 1 : 0;
+ }
+ }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 0cad5af00267..133d8ba8357b 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -27,20 +27,15 @@ import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageManager;
-import android.database.ContentObserver;
import android.ext.services.notification.AgingHelper.Callback;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationStats;
@@ -92,8 +87,6 @@ public class Assistant extends NotificationAssistantService {
PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL);
}
- private float mDismissToViewRatioLimit;
- private int mStreakLimit;
private SmartActionsHelper mSmartActionsHelper;
private NotificationCategorizer mNotificationCategorizer;
private AgingHelper mAgingHelper;
@@ -107,7 +100,11 @@ public class Assistant extends NotificationAssistantService {
private Ranking mFakeRanking = null;
private AtomicFile mFile = null;
private IPackageManager mPackageManager;
- protected SettingsObserver mSettingsObserver;
+
+ @VisibleForTesting
+ protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY;
+ @VisibleForTesting
+ protected AssistantSettings mSettings;
public Assistant() {
}
@@ -118,7 +115,8 @@ public class Assistant extends NotificationAssistantService {
// Contexts are correctly hooked up by the creation step, which is required for the observer
// to be hooked up/initialized.
mPackageManager = ActivityThread.getPackageManager();
- mSettingsObserver = new SettingsObserver(mHandler);
+ mSettings = mSettingsFactory.createAndRegister(mHandler,
+ getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
mSmartActionsHelper = new SmartActionsHelper();
mNotificationCategorizer = new NotificationCategorizer();
mAgingHelper = new AgingHelper(getContext(),
@@ -216,11 +214,11 @@ public class Assistant extends NotificationAssistantService {
if (!isForCurrentUser(sbn)) {
return null;
}
- NotificationEntry entry = new NotificationEntry(
- ActivityThread.getPackageManager(), sbn, channel);
+ NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel);
ArrayList<Notification.Action> actions =
- mSmartActionsHelper.suggestActions(this, entry);
- ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(this, entry);
+ mSmartActionsHelper.suggestActions(this, entry, mSettings);
+ ArrayList<CharSequence> replies =
+ mSmartActionsHelper.suggestReplies(this, entry, mSettings);
return createEnqueuedNotificationAdjustment(entry, actions, replies);
}
@@ -239,8 +237,7 @@ public class Assistant extends NotificationAssistantService {
if (!smartReplies.isEmpty()) {
signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
}
- if (Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
+ if (mSettings.mNewInterruptionModel) {
if (mNotificationCategorizer.shouldSilence(entry)) {
final int importance = entry.getImportance() < IMPORTANCE_LOW
? entry.getImportance() : IMPORTANCE_LOW;
@@ -460,6 +457,11 @@ public class Assistant extends NotificationAssistantService {
}
@VisibleForTesting
+ public void setSmartActionsHelper(SmartActionsHelper smartActionsHelper) {
+ mSmartActionsHelper = smartActionsHelper;
+ }
+
+ @VisibleForTesting
public ChannelImpressions getImpressions(String key) {
synchronized (mkeyToImpressions) {
return mkeyToImpressions.get(key);
@@ -475,10 +477,20 @@ public class Assistant extends NotificationAssistantService {
private ChannelImpressions createChannelImpressionsWithThresholds() {
ChannelImpressions impressions = new ChannelImpressions();
- impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit);
+ impressions.updateThresholds(mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
return impressions;
}
+ private void updateThresholds() {
+ // Update all existing channel impression objects with any new limits/thresholds.
+ synchronized (mkeyToImpressions) {
+ for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
+ channelImpressions.updateThresholds(
+ mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
+ }
+ }
+ }
+
protected final class AgingCallback implements Callback {
@Override
public void sendAdjustment(String key, int newImportance) {
@@ -495,51 +507,4 @@ public class Assistant extends NotificationAssistantService {
}
}
- /**
- * Observer for updates on blocking helper threshold values.
- */
- protected final class SettingsObserver extends ContentObserver {
- private final Uri STREAK_LIMIT_URI =
- Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
- private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
- Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
-
- public SettingsObserver(Handler handler) {
- super(handler);
- ContentResolver resolver = getApplicationContext().getContentResolver();
- resolver.registerContentObserver(
- DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId());
- resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId());
-
- // Update all uris on creation.
- update(null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- update(uri);
- }
-
- private void update(Uri uri) {
- ContentResolver resolver = getApplicationContext().getContentResolver();
- if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
- mDismissToViewRatioLimit = Settings.Global.getFloat(
- resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
- }
- if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
- mStreakLimit = Settings.Global.getInt(
- resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
- ChannelImpressions.DEFAULT_STREAK_LIMIT);
- }
-
- // Update all existing channel impression objects with any new limits/thresholds.
- synchronized (mkeyToImpressions) {
- for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
- channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit);
- }
- }
- }
- }
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
new file mode 100644
index 000000000000..39a1676b4ec7
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ext.services.notification;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Observes the settings for {@link Assistant}.
+ */
+final class AssistantSettings extends ContentObserver {
+ public static Factory FACTORY = AssistantSettings::createAndRegister;
+ private static final boolean DEFAULT_GENERATE_REPLIES = true;
+ private static final boolean DEFAULT_GENERATE_ACTIONS = true;
+ private static final int DEFAULT_NEW_INTERRUPTION_MODEL_INT = 1;
+
+ private static final Uri STREAK_LIMIT_URI =
+ Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
+ private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
+ private static final Uri SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS);
+ private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
+ Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
+
+ private static final String KEY_GENERATE_REPLIES = "generate_replies";
+ private static final String KEY_GENERATE_ACTIONS = "generate_actions";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private final ContentResolver mResolver;
+ private final int mUserId;
+
+ @VisibleForTesting
+ protected final Runnable mOnUpdateRunnable;
+
+ // Actuall configuration settings.
+ float mDismissToViewRatioLimit;
+ int mStreakLimit;
+ boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
+ boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
+ boolean mNewInterruptionModel;
+
+ private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
+ Runnable onUpdateRunnable) {
+ super(handler);
+ mResolver = resolver;
+ mUserId = userId;
+ mOnUpdateRunnable = onUpdateRunnable;
+ }
+
+ private static AssistantSettings createAndRegister(
+ Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
+ AssistantSettings assistantSettings =
+ new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
+ assistantSettings.register();
+ return assistantSettings;
+ }
+
+ /**
+ * Creates an instance but doesn't register it as an observer.
+ */
+ @VisibleForTesting
+ protected static AssistantSettings createForTesting(
+ Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
+ return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
+ }
+
+ private void register() {
+ mResolver.registerContentObserver(
+ DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
+ mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
+ mResolver.registerContentObserver(
+ SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI, false, this, mUserId);
+
+ // Update all uris on creation.
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ private void update(Uri uri) {
+ if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
+ mDismissToViewRatioLimit = Settings.Global.getFloat(
+ mResolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
+ ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
+ }
+ if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
+ mStreakLimit = Settings.Global.getInt(
+ mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
+ ChannelImpressions.DEFAULT_STREAK_LIMIT);
+ }
+ if (uri == null || SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI.equals(uri)) {
+ mParser.setString(
+ Settings.Global.getString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+ mGenerateReplies =
+ mParser.getBoolean(KEY_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
+ mGenerateActions =
+ mParser.getBoolean(KEY_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
+ }
+ if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
+ int mNewInterruptionModelInt = Settings.Secure.getInt(
+ mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
+ DEFAULT_NEW_INTERRUPTION_MODEL_INT);
+ mNewInterruptionModel = mNewInterruptionModelInt == 1;
+ }
+
+ mOnUpdateRunnable.run();
+ }
+
+ public interface Factory {
+ AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
+ Runnable onUpdateRunnable);
+ }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 892267b22058..6f2b6c9dafd4 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -69,8 +69,11 @@ public class SmartActionsHelper {
* from notification text / message, we can replace most of the code here by consuming that API.
*/
@NonNull
- ArrayList<Notification.Action> suggestActions(
- @Nullable Context context, @NonNull NotificationEntry entry) {
+ ArrayList<Notification.Action> suggestActions(@Nullable Context context,
+ @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
+ if (!settings.mGenerateActions) {
+ return EMPTY_ACTION_LIST;
+ }
if (!isEligibleForActionAdjustment(entry)) {
return EMPTY_ACTION_LIST;
}
@@ -86,8 +89,11 @@ public class SmartActionsHelper {
getMostSalientActionText(entry.getNotification()), MAX_SMART_ACTIONS);
}
- ArrayList<CharSequence> suggestReplies(
- @Nullable Context context, @NonNull NotificationEntry entry) {
+ ArrayList<CharSequence> suggestReplies(@Nullable Context context,
+ @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
+ if (!settings.mGenerateReplies) {
+ return EMPTY_REPLY_LIST;
+ }
if (!isEligibleForReplyAdjustment(entry)) {
return EMPTY_REPLY_LIST;
}
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
index 0a95b858a93e..a57fa9458f08 100644
--- a/packages/ExtServices/tests/Android.mk
+++ b/packages/ExtServices/tests/Android.mk
@@ -12,7 +12,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-target-minus-junit4 \
espresso-core \
truth-prebuilt \
- testables
+ testables \
+ testng
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
index 48c076e67e78..6fda4c73792b 100644
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
@@ -16,15 +16,22 @@
package android.ext.services.autofill;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Collections;
+import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
+import static android.view.autofill.AutofillValue.forText;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import android.os.Bundle;
import android.view.autofill.AutofillValue;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
/**
* Contains the base tests that does not rely on the specific algorithm implementation.
*/
@@ -34,26 +41,78 @@ public class AutofillFieldClassificationServiceImplTest {
new AutofillFieldClassificationServiceImpl();
@Test
- public void testOnGetScores_nullActualValues() {
- assertThat(mService.onGetScores(null, null, null, Arrays.asList("whatever"))).isNull();
+ public void testOnCalculateScores_nullActualValues() {
+ assertThat(mService.onCalculateScores(null, null, null, null, null, null, null)).isNull();
}
@Test
- public void testOnGetScores_emptyActualValues() {
- assertThat(mService.onGetScores(null, null, Collections.emptyList(),
- Arrays.asList("whatever"))).isNull();
+ public void testOnCalculateScores_emptyActualValues() {
+ assertThat(mService.onCalculateScores(Collections.emptyList(), Arrays.asList("whatever"),
+ null, null, null, null, null)).isNull();
}
@Test
- public void testOnGetScores_nullUserDataValues() {
- assertThat(mService.onGetScores(null, null,
- Arrays.asList(AutofillValue.forText("whatever")), null)).isNull();
+ public void testOnCalculateScores_nullUserDataValues() {
+ assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
+ null, null, null, null, null, null)).isNull();
}
@Test
- public void testOnGetScores_emptyUserDataValues() {
- assertThat(mService.onGetScores(null, null,
- Arrays.asList(AutofillValue.forText("whatever")), Collections.emptyList()))
- .isNull();
+ public void testOnCalculateScores_emptyUserDataValues() {
+ assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
+ Collections.emptyList(), null, null, null, null, null))
+ .isNull();
+ }
+
+ @Test
+ public void testCalculateScores() {
+ final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b"),
+ forText("dude"));
+ final List<String> userDataValues = Arrays.asList("a", "b", "B", "ab", "c", "dude",
+ "sweet_dude", "dude_sweet");
+ final List<String> categoryIds = Arrays.asList("cat", "cat", "cat", "cat", "cat", "last4",
+ "last4", "last4");
+ final HashMap<String, String> algorithms = new HashMap<>(1);
+ algorithms.put("last4", REQUIRED_ALGORITHM_EXACT_MATCH);
+
+ final Bundle last4Bundle = new Bundle();
+ last4Bundle.putInt("suffix", 4);
+
+ final HashMap<String, Bundle> args = new HashMap<>(1);
+ args.put("last4", last4Bundle);
+
+ final float[][] expectedScores = new float[][] {
+ new float[] { 1F, 0F, 0F, 0.5F, 0F, 0F, 0F, 0F },
+ new float[] { 0F, 1F, 1F, 0.5F, 0F, 0F, 0F, 0F },
+ new float[] { 0F, 0F, 0F, 0F , 0F, 1F, 1F, 0F }
+ };
+ final float[][] actualScores = mService.onCalculateScores(actualValues, userDataValues,
+ categoryIds, null, null, algorithms, args);
+
+ // Unfortunately, Truth does not have an easy way to compare float matrices and show useful
+ // messages in case of error, so we need to check.
+ assertWithMessage("actual=%s, expected=%s", toString(actualScores),
+ toString(expectedScores)).that(actualScores.length).isEqualTo(3);
+ for (int i = 0; i < 3; i++) {
+ assertWithMessage("actual=%s, expected=%s", toString(actualScores),
+ toString(expectedScores)).that(actualScores[i].length).isEqualTo(8);
+ }
+
+ for (int i = 0; i < actualScores.length; i++) {
+ final float[] line = actualScores[i];
+ for (int j = 0; j < line.length; j++) {
+ float cell = line[j];
+ assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F)
+ .of(expectedScores[i][j]);
+ }
+ }
+ }
+
+ public static String toString(float[][] matrix) {
+ final StringBuilder string = new StringBuilder("[ ");
+ for (int i = 0; i < matrix.length; i++) {
+ string.append(Arrays.toString(matrix[i])).append(" ");
+ }
+ return string.append(" ]").toString();
}
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
index afe223641d37..9b9d4be59929 100644
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
@@ -15,107 +15,67 @@
*/
package android.ext.services.autofill;
-import static android.ext.services.autofill.EditDistanceScorer.getScore;
-import static android.ext.services.autofill.EditDistanceScorer.getScores;
-import static android.view.autofill.AutofillValue.forText;
+import static android.ext.services.autofill.EditDistanceScorer.calculateScore;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import android.view.autofill.AutofillValue;
import org.junit.Test;
-import java.util.Arrays;
-import java.util.List;
-
public class EditDistanceScorerTest {
@Test
- public void testGetScore_nullValue() {
- assertFloat(getScore(null, "D'OH!"), 0);
+ public void testCalculateScore_nullValue() {
+ assertFloat(calculateScore(null, "D'OH!"), 0);
}
@Test
- public void testGetScore_nonTextValue() {
- assertFloat(getScore(AutofillValue.forToggle(true), "D'OH!"), 0);
+ public void testCalculateScore_nonTextValue() {
+ assertFloat(calculateScore(AutofillValue.forToggle(true), "D'OH!"), 0);
}
@Test
- public void testGetScore_nullUserData() {
- assertFloat(getScore(AutofillValue.forText("D'OH!"), null), 0);
+ public void testCalculateScore_nullUserData() {
+ assertFloat(calculateScore(AutofillValue.forText("D'OH!"), null), 0);
}
@Test
- public void testGetScore_fullMatch() {
- assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
- assertFloat(getScore(AutofillValue.forText(""), ""), 1);
+ public void testCalculateScore_fullMatch() {
+ assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
+ assertFloat(calculateScore(AutofillValue.forText(""), ""), 1);
}
@Test
- public void testGetScore_fullMatchMixedCase() {
- assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
+ public void testCalculateScore_fullMatchMixedCase() {
+ assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
}
@Test
- public void testGetScore_mismatchDifferentSizes() {
- assertFloat(getScore(AutofillValue.forText("X"), "Xy"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("Xy"), "X"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F);
- assertFloat(getScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F);
- assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Parkway"),
+ public void testCalculateScore_mismatchDifferentSizes() {
+ assertFloat(calculateScore(AutofillValue.forText("X"), "Xy"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("Xy"), "X"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F);
+ assertFloat(calculateScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F);
+ assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Parkway"),
"1600 Amphitheatre Pkwy"), 0.88F);
- assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Pkwy"),
+ assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Pkwy"),
"1600 Amphitheatre Parkway"), 0.88F);
}
@Test
- public void testGetScore_partialMatch() {
- assertFloat(getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
- assertFloat(getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
- assertFloat(getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
- assertFloat(getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
- assertFloat(getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
- }
-
- @Test
- public void testGetScores() {
- final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b"));
- final List<String> userDataValues = Arrays.asList("a", "B", "ab", "c");
- final float[][] expectedScores = new float[][] {
- new float[] { 1F, 0F, 0.5F, 0F },
- new float[] { 0F, 1F, 0.5F, 0F }
- };
- final float[][] actualScores = getScores(actualValues, userDataValues);
-
- // Unfortunately, Truth does not have an easy way to compare float matrices and show useful
- // messages in case of error, so we need to check.
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores.length).isEqualTo(2);
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores[0].length).isEqualTo(4);
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores[1].length).isEqualTo(4);
- for (int i = 0; i < actualScores.length; i++) {
- final float[] line = actualScores[i];
- for (int j = 0; j < line.length; j++) {
- float cell = line[j];
- assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F)
- .of(expectedScores[i][j]);
- }
- }
+ public void testCalculateScore_partialMatch() {
+ assertFloat(calculateScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
+ assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
+ assertFloat(calculateScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
+ assertFloat(calculateScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
+ assertFloat(calculateScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
}
public static void assertFloat(float actualValue, float expectedValue) {
assertThat(actualValue).isWithin(0.01F).of(expectedValue);
}
- public static String toString(float[][] matrix) {
- final StringBuilder string = new StringBuilder("[ ");
- for (int i = 0; i < matrix.length; i++) {
- string.append(Arrays.toString(matrix[i])).append(" ");
- }
- return string.append(" ]").toString();
- }
+
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
new file mode 100644
index 000000000000..bf5e1609fa8b
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.autofill;
+
+import static android.ext.services.autofill.ExactMatch.calculateScore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Bundle;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+
+public class ExactMatchTest {
+
+ private Bundle last4Bundle() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt("suffix", 4);
+ return bundle;
+ }
+
+ @Test
+ public void testCalculateScore_nullValue() {
+ assertFloat(calculateScore(null, "TEST", null), 0);
+ }
+
+ @Test
+ public void testCalculateScore_nonTextValue() {
+ assertFloat(calculateScore(AutofillValue.forToggle(true), "TEST", null), 0);
+ }
+
+ @Test
+ public void testCalculateScore_nullUserData() {
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), null, null), 0);
+ }
+
+ @Test
+ public void testCalculateScore_succeedMatchMixedCases_last4() {
+ final Bundle last4 = last4Bundle();
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "1234 test", last4), 1);
+ assertFloat(calculateScore(AutofillValue.forText("test"), "1234 TEST", last4), 1);
+ }
+
+ @Test
+ public void testCalculateScore_mismatchDifferentSizes_last4() {
+ final Bundle last4 = last4Bundle();
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST1", last4), 0);
+ assertFloat(calculateScore(AutofillValue.forText(""), "TEST", last4), 0);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "", last4), 0);
+ }
+
+ @Test
+ public void testCalculateScore_match() {
+ final Bundle last4 = last4Bundle();
+ assertFloat(calculateScore(AutofillValue.forText("1234 1234 1234 1234"),
+ "xxxx xxxx xxxx 1234", last4), 1);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", null), 1);
+ assertFloat(calculateScore(AutofillValue.forText("TEST 1234"), "1234", last4), 1);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "test", null), 1);
+ }
+
+ @Test
+ public void testCalculateScore_badBundle() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt("suffix", -2);
+ assertThrows(IllegalArgumentException.class, () -> calculateScore(
+ AutofillValue.forText("TEST"), "TEST", bundle));
+
+ final Bundle largeBundle = new Bundle();
+ largeBundle.putInt("suffix", 10);
+ assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", largeBundle), 1);
+
+ final Bundle stringBundle = new Bundle();
+ stringBundle.putString("suffix", "value");
+ assertThrows(IllegalArgumentException.class, () -> calculateScore(
+ AutofillValue.forText("TEST"), "TEST", stringBundle));
+
+ }
+
+ public static void assertFloat(float actualValue, float expectedValue) {
+ assertThat(actualValue).isWithin(0.01F).of(expectedValue);
+ }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
new file mode 100644
index 000000000000..fd23f2b78b42
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
@@ -0,0 +1,162 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ext.services.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class AssistantSettingsTest {
+ private static final int USER_ID = 5;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ @Mock Runnable mOnUpdateRunnable;
+
+ private ContentResolver mResolver;
+ private AssistantSettings mAssistantSettings;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mResolver = mContext.getContentResolver();
+ Handler handler = new Handler(Looper.getMainLooper());
+
+ // To bypass real calls to global settings values, set the Settings values here.
+ Settings.Global.putFloat(mResolver,
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
+ Settings.Global.putInt(mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ "generate_replies=true,generate_actions=true");
+ Settings.Secure.putInt(mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+
+ mAssistantSettings = AssistantSettings.createForTesting(
+ handler, mResolver, USER_ID, mOnUpdateRunnable);
+ }
+
+ @Test
+ public void testGenerateRepliesDisabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ "generate_replies=false");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+
+ assertFalse(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testGenerateRepliesEnabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_replies=true");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+ assertTrue(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testGenerateActionsDisabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=false");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+ assertTrue(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testGenerateActionsEnabled() {
+ Settings.Global.putString(mResolver,
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=true");
+
+ // Notify for the settings values we updated.
+ mAssistantSettings.onChange(false,
+ Settings.Global.getUriFor(
+ Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+ assertTrue(mAssistantSettings.mGenerateReplies);
+ }
+
+ @Test
+ public void testStreakLimit() {
+ verify(mOnUpdateRunnable, never()).run();
+
+ // Update settings value.
+ int newStreakLimit = 4;
+ Settings.Global.putInt(mResolver,
+ Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
+
+ // Notify for the settings value we updated.
+ mAssistantSettings.onChange(false, Settings.Global.getUriFor(
+ Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
+
+ assertEquals(newStreakLimit, mAssistantSettings.mStreakLimit);
+ verify(mOnUpdateRunnable).run();
+ }
+
+ @Test
+ public void testDismissToViewRatioLimit() {
+ verify(mOnUpdateRunnable, never()).run();
+
+ // Update settings value.
+ float newDismissToViewRatioLimit = 3f;
+ Settings.Global.putFloat(mResolver,
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
+ newDismissToViewRatioLimit);
+
+ // Notify for the settings value we updated.
+ mAssistantSettings.onChange(false, Settings.Global.getUriFor(
+ Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
+
+ assertEquals(newDismissToViewRatioLimit, mAssistantSettings.mDismissToViewRatioLimit, 1e-6);
+ verify(mOnUpdateRunnable).run();
+ }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 2eb005a9b1fa..0a95b83bdbe3 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -33,13 +33,11 @@ import android.app.Application;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
import android.os.UserHandle;
-import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -86,8 +84,7 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
@Mock INotificationManager mNoMan;
@Mock AtomicFile mFile;
- @Mock
- IPackageManager mPackageManager;
+ @Mock IPackageManager mPackageManager;
Assistant mAssistant;
Application mApplication;
@@ -108,20 +105,26 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
new Intent("android.service.notification.NotificationAssistantService");
startIntent.setPackage("android.ext.services");
- // To bypass real calls to global settings values, set the Settings values here.
- Settings.Global.putFloat(mContext.getContentResolver(),
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
mApplication = (Application) InstrumentationRegistry.getInstrumentation().
getTargetContext().getApplicationContext();
// Force the test to use the correct application instead of trying to use a mock application
setApplication(mApplication);
- bindService(startIntent);
+
+ setupService();
mAssistant = getService();
+
+ // Override the AssistantSettings factory.
+ mAssistant.mSettingsFactory = AssistantSettings::createForTesting;
+
+ bindService(startIntent);
+
+ mAssistant.mSettings.mDismissToViewRatioLimit = 0.8f;
+ mAssistant.mSettings.mStreakLimit = 2;
+ mAssistant.mSettings.mNewInterruptionModel = true;
mAssistant.setNoMan(mNoMan);
mAssistant.setFile(mFile);
mAssistant.setPackageManager(mPackageManager);
+
ApplicationInfo info = mock(ApplicationInfo.class);
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
.thenReturn(info);
@@ -408,6 +411,8 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
mAssistant.writeXml(serializer);
Assistant assistant = new Assistant();
+ // onCreate is not invoked, so settings won't be initialised, unless we do it here.
+ assistant.mSettings = mAssistant.mSettings;
assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
assertEquals(ci1, assistant.getImpressions(key1));
@@ -417,8 +422,6 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
@Test
public void testSettingsProviderUpdate() {
- ContentResolver resolver = mApplication.getContentResolver();
-
// Set up channels
String key = mAssistant.getKey("pkg1", 1, "channel1");
ChannelImpressions ci = new ChannelImpressions();
@@ -435,19 +438,11 @@ public class AssistantTest extends ServiceTestCase<Assistant> {
assertEquals(false, ci.shouldTriggerBlock());
// Update settings values.
- float newDismissToViewRatioLimit = 0f;
- int newStreakLimit = 0;
- Settings.Global.putFloat(resolver,
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- newDismissToViewRatioLimit);
- Settings.Global.putInt(resolver,
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
+ mAssistant.mSettings.mDismissToViewRatioLimit = 0f;
+ mAssistant.mSettings.mStreakLimit = 0;
// Notify for the settings values we updated.
- mAssistant.mSettingsObserver.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
- mAssistant.mSettingsObserver.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
+ mAssistant.mSettings.mOnUpdateRunnable.run();
// With the new threshold, the blocking helper should be triggered.
assertEquals(true, ci.shouldTriggerBlock());
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 87d6e4a137ac..be817d60e55b 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -23,7 +23,6 @@ import android.content.IntentFilter;
import android.location.Criteria;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -34,87 +33,53 @@ import com.android.location.provider.ProviderRequestUnbundled;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
+class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
private static final String TAG = "FusedLocationProvider";
private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
false, false, false, false, true, true, true, Criteria.POWER_LOW,
Criteria.ACCURACY_FINE);
- private static final int MSG_ENABLE = 1;
- private static final int MSG_DISABLE = 2;
- private static final int MSG_SET_REQUEST = 3;
-
+ private final Context mContext;
+ private final Handler mHandler;
private final FusionEngine mEngine;
- private static class RequestWrapper {
- public ProviderRequestUnbundled request;
- public WorkSource source;
- public RequestWrapper(ProviderRequestUnbundled request, WorkSource source) {
- this.request = request;
- this.source = source;
+ private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mEngine.switchUser();
+ }
}
- }
+ };
- public FusedLocationProvider(Context context) {
+ FusedLocationProvider(Context context) {
super(TAG, PROPERTIES);
- mEngine = new FusionEngine(context, Looper.myLooper());
- // listen for user change
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- context.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mEngine.switchUser();
- }
- }
- }, UserHandle.ALL, intentFilter, null, mHandler);
+ mContext = context;
+ mHandler = new Handler(Looper.myLooper());
+ mEngine = new FusionEngine(context, Looper.myLooper(), this);
}
- /**
- * For serializing requests to mEngine.
- */
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ENABLE:
- mEngine.init(FusedLocationProvider.this);
- break;
- case MSG_DISABLE:
- mEngine.deinit();
- break;
- case MSG_SET_REQUEST:
- {
- RequestWrapper wrapper = (RequestWrapper) msg.obj;
- mEngine.setRequest(wrapper.request, wrapper.source);
- break;
- }
- }
- }
- };
-
- @Override
- public void onEnable() {
- mHandler.sendEmptyMessage(MSG_ENABLE);
+ void init() {
+ // listen for user change
+ mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
}
- @Override
- public void onDisable() {
- mHandler.sendEmptyMessage(MSG_DISABLE);
+ void destroy() {
+ mContext.unregisterReceiver(mUserSwitchReceiver);
+ mHandler.post(() -> mEngine.setRequest(null));
}
@Override
public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) {
- mHandler.obtainMessage(MSG_SET_REQUEST, new RequestWrapper(request, source)).sendToTarget();
+ mHandler.post(() -> mEngine.setRequest(request));
}
@Override
public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- // perform synchronously
mEngine.dump(fd, pw, args);
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
index 12966cfab888..75bb5eceab6d 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
@@ -21,27 +21,24 @@ import android.content.Intent;
import android.os.IBinder;
public class FusedLocationService extends Service {
+
private FusedLocationProvider mProvider;
@Override
public IBinder onBind(Intent intent) {
if (mProvider == null) {
- mProvider = new FusedLocationProvider(getApplicationContext());
+ mProvider = new FusedLocationProvider(this);
+ mProvider.init();
}
+
return mProvider.getBinder();
}
@Override
- public boolean onUnbind(Intent intent) {
- // make sure to stop performing work
+ public void onDestroy() {
if (mProvider != null) {
- mProvider.onDisable();
+ mProvider.destroy();
+ mProvider = null;
}
- return false;
- }
-
- @Override
- public void onDestroy() {
- mProvider = null;
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7a4952484e46..e4610cf44636 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -16,14 +16,6 @@
package com.android.location.fused;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
@@ -32,9 +24,16 @@ import android.os.Bundle;
import android.os.Looper;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.os.WorkSource;
import android.util.Log;
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.LocationRequestUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
public class FusionEngine implements LocationListener {
public interface Callback {
void reportLocation(Location location);
@@ -47,72 +46,35 @@ public class FusionEngine implements LocationListener {
public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds
- private final Context mContext;
private final LocationManager mLocationManager;
private final Looper mLooper;
+ private final Callback mCallback;
// all fields are only used on mLooper thread. except for in dump() which is not thread-safe
- private Callback mCallback;
private Location mFusedLocation;
private Location mGpsLocation;
private Location mNetworkLocation;
- private boolean mEnabled;
private ProviderRequestUnbundled mRequest;
private final HashMap<String, ProviderStats> mStats = new HashMap<>();
- public FusionEngine(Context context, Looper looper) {
- mContext = context;
+ FusionEngine(Context context, Looper looper, Callback callback) {
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
mNetworkLocation = new Location("");
mNetworkLocation.setAccuracy(Float.MAX_VALUE);
mGpsLocation = new Location("");
mGpsLocation.setAccuracy(Float.MAX_VALUE);
mLooper = looper;
+ mCallback = callback;
mStats.put(GPS, new ProviderStats());
mStats.put(NETWORK, new ProviderStats());
-
- }
-
- public void init(Callback callback) {
- Log.i(TAG, "engine started (" + mContext.getPackageName() + ")");
- mCallback = callback;
- }
-
- /**
- * Called to stop doing any work, and release all resources
- * This can happen when a better fusion engine is installed
- * in a different package, and this one is no longer needed.
- * Called on mLooper thread
- */
- public void deinit() {
- mRequest = null;
- disable();
- Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
- }
-
- /** Called on mLooper thread */
- public void enable() {
- if (!mEnabled) {
- mEnabled = true;
- updateRequirements();
- }
- }
-
- /** Called on mLooper thread */
- public void disable() {
- if (mEnabled) {
- mEnabled = false;
- updateRequirements();
- }
}
/** Called on mLooper thread */
- public void setRequest(ProviderRequestUnbundled request, WorkSource source) {
+ public void setRequest(ProviderRequestUnbundled request) {
mRequest = request;
- mEnabled = request.getReportLocation();
updateRequirements();
}
@@ -120,6 +82,7 @@ public class FusionEngine implements LocationListener {
public boolean requested;
public long requestTime;
public long minTime;
+
@Override
public String toString() {
return (requested ? " REQUESTED" : " ---");
@@ -154,7 +117,7 @@ public class FusionEngine implements LocationListener {
}
private void updateRequirements() {
- if (!mEnabled || mRequest == null) {
+ if (mRequest == null || !mRequest.getReportLocation()) {
mRequest = null;
disableProvider(NETWORK);
disableProvider(GPS);
@@ -200,29 +163,30 @@ public class FusionEngine implements LocationListener {
* Test whether one location (a) is better to use than another (b).
*/
private static boolean isBetterThan(Location locationA, Location locationB) {
- if (locationA == null) {
- return false;
- }
- if (locationB == null) {
- return true;
- }
- // A provider is better if the reading is sufficiently newer. Heading
- // underground can cause GPS to stop reporting fixes. In this case it's
- // appropriate to revert to cell, even when its accuracy is less.
- if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
- return true;
- }
-
- // A provider is better if it has better accuracy. Assuming both readings
- // are fresh (and by that accurate), choose the one with the smaller
- // accuracy circle.
- if (!locationA.hasAccuracy()) {
- return false;
- }
- if (!locationB.hasAccuracy()) {
- return true;
- }
- return locationA.getAccuracy() < locationB.getAccuracy();
+ if (locationA == null) {
+ return false;
+ }
+ if (locationB == null) {
+ return true;
+ }
+ // A provider is better if the reading is sufficiently newer. Heading
+ // underground can cause GPS to stop reporting fixes. In this case it's
+ // appropriate to revert to cell, even when its accuracy is less.
+ if (locationA.getElapsedRealtimeNanos()
+ > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
+ return true;
+ }
+
+ // A provider is better if it has better accuracy. Assuming both readings
+ // are fresh (and by that accurate), choose the one with the smaller
+ // accuracy circle.
+ if (!locationA.hasAccuracy()) {
+ return false;
+ }
+ if (!locationB.hasAccuracy()) {
+ return true;
+ }
+ return locationA.getAccuracy() < locationB.getAccuracy();
}
private void updateFusedLocation() {
@@ -252,9 +216,9 @@ public class FusionEngine implements LocationListener {
}
if (mCallback != null) {
- mCallback.reportLocation(mFusedLocation);
+ mCallback.reportLocation(mFusedLocation);
} else {
- Log.w(TAG, "Location updates received while fusion engine not started");
+ Log.w(TAG, "Location updates received while fusion engine not started");
}
}
@@ -272,19 +236,22 @@ public class FusionEngine implements LocationListener {
/** Called on mLooper thread */
@Override
- public void onStatusChanged(String provider, int status, Bundle extras) { }
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
/** Called on mLooper thread */
@Override
- public void onProviderEnabled(String provider) { }
+ public void onProviderEnabled(String provider) {
+ }
/** Called on mLooper thread */
@Override
- public void onProviderDisabled(String provider) { }
+ public void onProviderDisabled(String provider) {
+ }
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
- s.append("mEnabled=").append(mEnabled).append(' ').append(mRequest).append('\n');
+ s.append(mRequest).append('\n');
s.append("fused=").append(mFusedLocation).append('\n');
s.append(String.format("gps %s\n", mGpsLocation));
s.append(" ").append(mStats.get(GPS)).append('\n');
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 8f254e9735da..a7de631cd6fa 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -200,7 +200,7 @@ class MtpManager {
}
if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
- sendObjectInfoResult.getCompressedSize(), source)) {
+ sendObjectInfoResult.getCompressedSizeLong(), source)) {
throw new IOException("Failed to send contents of a document");
}
}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index eb9ec82c9519..18b86628ea4d 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="com.google.android.permission.INSTALL_WEARABLE_PACKAGES" />
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
new file mode 100644
index 000000000000..5ecb614e7209
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Check box that is displayed in the activity resolver UI for the user
+ to make their selection the preferred activity. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <CheckBox
+ android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index d751c42a2df7..01f3e5b5bd82 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -22,17 +22,17 @@
<string name="cancel" msgid="1018267193425558088">"रद्द करा"</string>
<string name="installing" msgid="4921993079741206516">"इंस्‍टॉल होत आहे…"</string>
<string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> इंस्टॉल होत आहे…"</string>
- <string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
+ <string name="install_done" msgid="5987363587661783896">"अ‍ॅप इंस्टॉल झाले."</string>
<string name="install_confirm_question" msgid="8176284075816604590">"तुम्हाला हे अॅप्लिकेशन इंस्टॉल करायचे आहे का?"</string>
<string name="install_confirm_question_update" msgid="7942235418781274635">"तुम्हाच्या विद्यमान अॅप्लिकेशनवर अपडेट इंस्टॉल करायचे आहे का? तुमचा विद्यमान डेटा गमावणार नाही."</string>
<string name="install_confirm_question_update_system" msgid="4713001702777910263">"तुम्हाला या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायचे आहे का? तुमचा विद्यमान डेटा गमावणार नाही."</string>
- <string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed" msgid="5777824004474125469">"अ‍ॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पॅकेज इंस्टॉल होण्यापासून ब्लॉक केले होते."</string>
- <string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"तुमच्या टॅबलेटशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"हे अॅप तुमच्या टीव्हीशी कंपॅटिबल नाही."</string>
- <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"तुमच्या फोनशी कंपॅटिबल नसल्याने अॅप इंस्टॉल झाले नाही."</string>
- <string name="install_failed_invalid_apk" msgid="8581007676422623930">"पॅकेज अयोग्य असल्याचे दिसत असल्याने अॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अ‍ॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"तुमच्या टॅबलेटशी कंपॅटिबल नसल्याने अ‍ॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"हे अ‍ॅप तुमच्या टीव्हीशी कंपॅटिबल नाही."</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"तुमच्या फोनशी कंपॅटिबल नसल्याने अ‍ॅप इंस्टॉल झाले नाही."</string>
+ <string name="install_failed_invalid_apk" msgid="8581007676422623930">"पॅकेज अयोग्य असल्याचे दिसत असल्याने अ‍ॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या टॅबलेटवर इंस्टॉल केले जाऊ शकत नाही."</string>
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या टीव्हीवर इंस्टॉल केले जाऊ शकत नाही."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"<xliff:g id="APP_NAME">%1$s</xliff:g> तुमच्या फोनवर इंस्टॉल केले जाऊ शकत नाही."</string>
@@ -44,20 +44,20 @@
<string name="manage_applications" msgid="5400164782453975580">"अ‍ॅप्स व्यवस्थापन"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"जागा संपली"</string>
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> इंस्टॉल केले जाऊ शकत नाही. काही जागा मोकळी करा आणि पुन्हा प्रयत्न करा."</string>
- <string name="app_not_found_dlg_title" msgid="5107924008597470285">"अॅप आढळले नाही"</string>
- <string name="app_not_found_dlg_text" msgid="5219983779377811611">"इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळले नाही."</string>
+ <string name="app_not_found_dlg_title" msgid="5107924008597470285">"अ‍ॅप आढळले नाही"</string>
+ <string name="app_not_found_dlg_text" msgid="5219983779377811611">"इंस्टॉल केलेल्या अ‍ॅप्सच्या सूचीमध्ये अ‍ॅप आढळले नाही."</string>
<string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"अनुमती नाही"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"हे अनइंस्टॉल करण्याची विद्यमान वापरकर्त्यास अनुमती नाही."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"एरर"</string>
- <string name="generic_error_dlg_text" msgid="5287861443265795232">"अॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
- <string name="uninstall_application_title" msgid="4045420072401428123">"अॅप अनइंस्टॉल करा"</string>
+ <string name="generic_error_dlg_text" msgid="5287861443265795232">"अ‍ॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"अ‍ॅप अनइंस्टॉल करा"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"अपडेट अनइंस्टॉल करा"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> खालील अॅपचा भाग आहे:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अ‍ॅप अनइंस्टॉल करायचे आहे का?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अ‍ॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अ‍ॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"तुम्हाला <xliff:g id="USERNAME">%1$s</xliff:g> वापरकर्त्यासाठी हे अ‍ॅप अनइंस्टॉल करायचे आहे का?"</string>
- <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"फॅक्टरी आवृत्तीसह हे अ‍ॅप बदलायचे का? सर्व डेटा काढला जाईल."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"फॅक्टरी आवृत्तीसह हे अ‍ॅप बदलायचे? सर्व डेटा काढला जाईल. हे कार्य प्रोफाइल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"अनइंस्टॉल रन होत आहेत"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"अनइंस्टॉल करता आले नाही"</string>
<string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल करत आहे…"</string>
@@ -68,7 +68,7 @@
<string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल करता आले नाही."</string>
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"अॅक्टिव्ह डिव्हाइस प्रशासक अ‍ॅप अनइंस्टॉल करू शकत नाही"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी अॅक्टिव्ह डिव्हाइस प्रशासक अ‍ॅप अनइंस्टॉल करू शकत नाही"</string>
- <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"हे अॅप काही वापरकर्ते किंवा प्रोफाइलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल करण्यात आले"</string>
+ <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"हे अ‍ॅप काही वापरकर्ते किंवा प्रोफाइलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल करण्यात आले"</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"तुमच्या प्रोफाइलसाठी हे अ‍ॅप आवश्यक आहे आणि अनइंस्टॉल केले जाऊ शकत नाही."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"तुमच्या डिव्हाइस प्रशासकास हे अ‍ॅप आवश्यक आहे आणि ते अनइंस्टॉल केले जाऊ शकत नाही."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"डिव्हाइस प्रशासक अ‍ॅप्स व्यवस्थापित करा"</string>
@@ -77,18 +77,18 @@
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"पॅकेज पार्स करण्यात समस्या आली."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
<string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"इंस्टॉल करा/अनइंस्टॉल करा क्रिया Wear वर सपोर्ट करत नाहीत."</string>
- <string name="message_staging" msgid="8032722385658438567">"अॅप सुरुवातीच्या स्थितीत आहे…"</string>
+ <string name="message_staging" msgid="8032722385658438567">"अ‍ॅप सुरुवातीच्या स्थितीत आहे…"</string>
<string name="app_name_unknown" msgid="6881210203354323926">"अज्ञात"</string>
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"तुमच्या सुरक्षिततेसाठी, तुमच्या टॅबलेटला या स्रोताकडील अज्ञात अ‍ॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"तुमच्या सुरक्षिततेसाठी, तुमच्या टीव्हीला या स्रोताकडील अज्ञात अ‍ॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"तुमच्या सुरक्षिततेसाठी, तुमच्या फोनला या स्रोताकडील अज्ञात अ‍ॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्‍सकडून होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्‍ही सहमती देता की ते वापरल्‍याने होणार्‍या तुमच्‍या फोनचे कोणत्‍याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्‍सकडून होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्‍ही सहमती देता की ते वापरल्‍याने तुमच्‍या टॅबलेटचे कोणत्‍याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्‍ही आणि वैयक्तिक डेटा अज्ञात अॅप्‍सकडून होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्‍याने तुमच्‍या टीव्‍हीचे कोणत्‍याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अ‍ॅप्‍सकडून होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हे अ‍ॅप इंस्टॉल करून, तुम्‍ही सहमती देता की ते वापरल्‍याने होणार्‍या तुमच्‍या फोनचे कोणत्‍याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अ‍ॅप्‍सकडून होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हे अ‍ॅप इंस्टॉल करून, तुम्‍ही सहमती देता की ते वापरल्‍याने तुमच्‍या टॅबलेटचे कोणत्‍याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्‍ही आणि वैयक्तिक डेटा अज्ञात अ‍ॅप्‍सकडून होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हे अ‍ॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्‍याने तुमच्‍या टीव्‍हीचे कोणत्‍याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"सुरू ठेवा"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग्ज"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear अ‍ॅप्स इंस्टॉल/अनइंस्टॉल करत आहे"</string>
- <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अॅप इंस्टॉल सूचना"</string>
+ <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अ‍ॅप इंस्टॉल सूचना"</string>
<string name="notification_installation_success_message" msgid="6450467996056038442">"यशस्वीरित्या इंस्टॉल केले"</string>
<string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" यशस्वीरित्या इंस्टॉल झाले"</string>
</resources>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 0f065ab95e2d..1d8747a1fda6 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -119,6 +119,8 @@
<string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
<!-- [CHAR LIMIT=none] -->
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
+ <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+ <string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string>
<!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
<string name="uninstalling_notification_channel">Running uninstalls</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index 1c0aec18dd30..d13bb65604af 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -50,6 +50,8 @@ public class UninstallUninstalling extends Activity implements
"com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+ static final String EXTRA_CLEAR_CONTRIBUTED_FILES =
+ "com.android.packageinstaller.extra.CLEAR_CONTRIBUTED_FILES";
private int mUninstallId;
private ApplicationInfo mAppInfo;
@@ -72,6 +74,8 @@ public class UninstallUninstalling extends Activity implements
if (savedInstanceState == null) {
boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
false);
+ boolean clearContributedFiles = getIntent().getBooleanExtra(
+ EXTRA_CLEAR_CONTRIBUTED_FILES, false);
UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER);
// Show dialog, which is the whole UI
@@ -95,12 +99,15 @@ public class UninstallUninstalling extends Activity implements
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
+ flags |= clearContributedFiles ? PackageManager.DELETE_CONTRIBUTED_MEDIA : 0;
+
try {
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mAppInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
- getPackageName(), allUsers ? PackageManager.DELETE_ALL_USERS : 0,
- pendingIntent.getIntentSender(), user.getIdentifier());
+ getPackageName(), flags, pendingIntent.getIntentSender(),
+ user.getIdentifier());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 1a01dc019bf7..0fa8c9a688c7 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -285,7 +285,7 @@ public class UninstallerActivity extends Activity {
fragment.show(ft, "dialog");
}
- public void startUninstallProgress() {
+ public void startUninstallProgress(boolean clearContributedFiles) {
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
@@ -310,6 +310,8 @@ public class UninstallerActivity extends Activity {
newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
+ newIntent.putExtra(UninstallUninstalling.EXTRA_CLEAR_CONTRIBUTED_FILES,
+ clearContributedFiles);
newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
if (returnResult) {
@@ -358,12 +360,14 @@ public class UninstallerActivity extends Activity {
try {
Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
+ int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0;
+ flags |= clearContributedFiles ? PackageManager.DELETE_CONTRIBUTED_MEDIA : 0;
+
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mDialogInfo.appInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
- getPackageName(), mDialogInfo.allUsers
- ? PackageManager.DELETE_ALL_USERS : 0,
- pendingIntent.getIntentSender(), mDialogInfo.user.getIdentifier());
+ getPackageName(), flags, pendingIntent.getIntentSender(),
+ mDialogInfo.user.getIdentifier());
} catch (Exception e) {
notificationManager.cancel(uninstallId);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index e0ca74eb99c9..e4e12759211d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -16,6 +16,9 @@
package com.android.packageinstaller.handheld;
+import static android.text.format.Formatter.formatFileSize;
+
+import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -24,14 +27,41 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.MediaStore;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
import com.android.packageinstaller.R;
import com.android.packageinstaller.UninstallerActivity;
+import java.io.IOException;
+import java.util.List;
+
public class UninstallAlertDialogFragment extends DialogFragment implements
DialogInterface.OnClickListener {
+ private CheckBox mClearContributedFiles;
+
+ /**
+ * Get number of bytes of the combined files contributed by the package.
+ *
+ * @param pkg The package that might have contibuted files.
+ * @param user The user the package belongs to.
+ *
+ * @return The number of bytes.
+ */
+ private long getContributedMediaSize(@NonNull String pkg, @NonNull UserHandle user) {
+ try {
+ return MediaStore.getContributedMediaSize(getContext(), pkg, user);
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final PackageManager pm = getActivity().getPackageManager();
@@ -76,14 +106,44 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
dialogBuilder.setTitle(appLabel);
dialogBuilder.setPositiveButton(android.R.string.ok, this);
dialogBuilder.setNegativeButton(android.R.string.cancel, this);
- dialogBuilder.setMessage(messageBuilder.toString());
+
+ String pkg = dialogInfo.appInfo.packageName;
+ long contributedFileSize = 0;
+ if (dialogInfo.allUsers) {
+ List<UserInfo> users = userManager.getUsers();
+
+ int numUsers = users.size();
+ for (int i = 0; i < numUsers; i++) {
+ UserHandle user = UserHandle.of(users.get(i).id);
+
+ contributedFileSize += getContributedMediaSize(pkg, user);
+ }
+ } else {
+ contributedFileSize = getContributedMediaSize(pkg, dialogInfo.user);
+ }
+
+ if (contributedFileSize == 0) {
+ dialogBuilder.setMessage(messageBuilder.toString());
+ } else {
+ LayoutInflater inflater = getContext().getSystemService(LayoutInflater.class);
+ ViewGroup content = (ViewGroup) inflater.inflate(R.layout.uninstall_content_view, null);
+
+ ((TextView) content.requireViewById(R.id.message)).setText(messageBuilder.toString());
+ mClearContributedFiles = content.requireViewById(R.id.checkbox);
+ mClearContributedFiles.setText(getString(R.string.uninstall_remove_contributed_files,
+ formatFileSize(getContext(), contributedFileSize)));
+
+ dialogBuilder.setView(content);
+ }
+
return dialogBuilder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
- ((UninstallerActivity) getActivity()).startUninstallProgress();
+ ((UninstallerActivity) getActivity()).startUninstallProgress(
+ mClearContributedFiles != null && mClearContributedFiles.isChecked());
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
index 828e5dbcfe5b..21d25f5b030f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -99,7 +99,7 @@ public class UninstallAlertFragment extends GuidedStepFragment {
public void onGuidedActionClicked(GuidedAction action) {
if (isAdded()) {
if (action.getId() == GuidedAction.ACTION_ID_OK) {
- ((UninstallerActivity) getActivity()).startUninstallProgress();
+ ((UninstallerActivity) getActivity()).startUninstallProgress(false);
getActivity().finish();
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index cc17b25d9a40..0126e7e59915 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -18,6 +18,7 @@ android_library {
"SettingsLibSettingsSpinner",
"SettingsLayoutPreference",
"ActionButtonsPreference",
+ "SettingsLibEntityHeaderWidgets",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
new file mode 100644
index 000000000000..3ca4ecd33ce4
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
@@ -0,0 +1,14 @@
+android_library {
+ name: "SettingsLibEntityHeaderWidgets",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibAppPreference"
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
new file mode 100644
index 000000000000..4b9f1ab8d6cc
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
new file mode 100644
index 000000000000..9f30eda242f6
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_entities_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="24dp"
+ android:paddingEnd="8dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:textAppearance="@style/AppEntitiesHeader.Text.HeaderTitle"/>
+
+ <LinearLayout
+ android:id="@+id/all_apps_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:gravity="center">
+
+ <include
+ android:id="@+id/app1_view"
+ layout="@layout/app_view"/>
+
+ <include
+ android:id="@+id/app2_view"
+ layout="@layout/app_view"/>
+
+ <include
+ android:id="@+id/app3_view"
+ layout="@layout/app_view"/>
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/header_details"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
new file mode 100644
index 000000000000..fcafa3140955
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginEnd="16dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"
+ android:layout_marginBottom="12dp"/>
+
+ <TextView
+ android:id="@+id/app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/AppEntitiesHeader.Text.Title"/>
+
+ <TextView
+ android:id="@+id/app_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/AppEntitiesHeader.Text.Summary"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml b/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml
new file mode 100644
index 000000000000..0eefd4bff97f
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <style name="AppEntitiesHeader.Text"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="AppEntitiesHeader.Text.HeaderTitle">
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="AppEntitiesHeader.Text.Title">
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="AppEntitiesHeader.Text.Summary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
new file mode 100644
index 000000000000..8ccf89fc38b0
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * This is used to initialize view which was inflated
+ * from {@link R.xml.app_entities_header.xml}.
+ *
+ * <p>The view looks like below.
+ *
+ * <pre>
+ * --------------------------------------------------------------
+ * | Header title |
+ * --------------------------------------------------------------
+ * | App1 icon | App2 icon | App3 icon |
+ * | App1 title | App2 title | App3 title |
+ * | App1 summary | App2 summary | App3 summary |
+ * |-------------------------------------------------------------
+ * | Header details |
+ * --------------------------------------------------------------
+ * </pre>
+ *
+ * <p>How to use AppEntitiesHeaderController?
+ *
+ * <p>1. Add a {@link LayoutPreference} in layout XML file.
+ * <pre>
+ * &lt;com.android.settingslib.widget.LayoutPreference
+ * android:key="app_entities_header"
+ * android:layout="@layout/app_entities_header"/&gt;
+ * </pre>
+ *
+ * <p>2. Use AppEntitiesHeaderController to call below methods, then you can initialize
+ * view of <code>app_entities_header</code>.
+ *
+ * <pre>
+ *
+ * View headerView = ((LayoutPreference) screen.findPreference("app_entities_header"))
+ * .findViewById(R.id.app_entities_header);
+ *
+ * AppEntitiesHeaderController.newInstance(context, headerView)
+ * .setHeaderTitleRes(R.string.xxxxx)
+ * .setHeaderDetailsRes(R.string.xxxxx)
+ * .setHeaderDetailsClickListener(onClickListener)
+ * .setAppEntity(0, icon, "app title", "app summary")
+ * .setAppEntity(1, icon, "app title", "app summary")
+ * .setAppEntity(2, icon, "app title", "app summary")
+ * .apply();
+ * </pre>
+ */
+public class AppEntitiesHeaderController {
+
+ private static final String TAG = "AppEntitiesHeaderCtl";
+
+ @VisibleForTesting
+ static final int MAXIMUM_APPS = 3;
+
+ private final Context mContext;
+ private final TextView mHeaderTitleView;
+ private final Button mHeaderDetailsView;
+
+ private final AppEntity[] mAppEntities;
+ private final View[] mAppEntityViews;
+ private final ImageView[] mAppIconViews;
+ private final TextView[] mAppTitleViews;
+ private final TextView[] mAppSummaryViews;
+
+ private int mHeaderTitleRes;
+ private int mHeaderDetailsRes;
+ private View.OnClickListener mDetailsOnClickListener;
+
+ /**
+ * Creates a new instance of the controller.
+ *
+ * @param context the Context the view is running in
+ * @param appEntitiesHeaderView view was inflated from <code>app_entities_header</code>
+ */
+ public static AppEntitiesHeaderController newInstance(@NonNull Context context,
+ @NonNull View appEntitiesHeaderView) {
+ return new AppEntitiesHeaderController(context, appEntitiesHeaderView);
+ }
+
+ private AppEntitiesHeaderController(Context context, View appEntitiesHeaderView) {
+ mContext = context;
+ mHeaderTitleView = appEntitiesHeaderView.findViewById(R.id.header_title);
+ mHeaderDetailsView = appEntitiesHeaderView.findViewById(R.id.header_details);
+
+ mAppEntities = new AppEntity[MAXIMUM_APPS];
+ mAppIconViews = new ImageView[MAXIMUM_APPS];
+ mAppTitleViews = new TextView[MAXIMUM_APPS];
+ mAppSummaryViews = new TextView[MAXIMUM_APPS];
+
+ mAppEntityViews = new View[]{
+ appEntitiesHeaderView.findViewById(R.id.app1_view),
+ appEntitiesHeaderView.findViewById(R.id.app2_view),
+ appEntitiesHeaderView.findViewById(R.id.app3_view)
+ };
+
+ // Initialize view in advance, so we won't take too much time to do it when controller is
+ // binding view.
+ for (int index = 0; index < MAXIMUM_APPS; index++) {
+ final View appView = mAppEntityViews[index];
+ mAppIconViews[index] = (ImageView) appView.findViewById(R.id.app_icon);
+ mAppTitleViews[index] = (TextView) appView.findViewById(R.id.app_title);
+ mAppSummaryViews[index] = (TextView) appView.findViewById(R.id.app_summary);
+ }
+ }
+
+ /**
+ * Set the text resource for app entities header title.
+ */
+ public AppEntitiesHeaderController setHeaderTitleRes(@StringRes int titleRes) {
+ mHeaderTitleRes = titleRes;
+ return this;
+ }
+
+ /**
+ * Set the text resource for app entities header details.
+ */
+ public AppEntitiesHeaderController setHeaderDetailsRes(@StringRes int detailsRes) {
+ mHeaderDetailsRes = detailsRes;
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when header details view is clicked.
+ */
+ public AppEntitiesHeaderController setHeaderDetailsClickListener(
+ @Nullable View.OnClickListener clickListener) {
+ mDetailsOnClickListener = clickListener;
+ return this;
+ }
+
+ /**
+ * Set an app entity at a specified position view.
+ *
+ * @param index the index at which the specified view is to be inserted
+ * @param icon the icon of app entity
+ * @param titleRes the title of app entity
+ * @param summaryRes the summary of app entity
+ * @return this {@code AppEntitiesHeaderController} object
+ */
+ public AppEntitiesHeaderController setAppEntity(int index, @NonNull Drawable icon,
+ @Nullable CharSequence titleRes, @Nullable CharSequence summaryRes) {
+ final AppEntity appEntity = new AppEntity(icon, titleRes, summaryRes);
+ mAppEntities[index] = appEntity;
+ return this;
+ }
+
+ /**
+ * Remove an app entity at a specified position view.
+ *
+ * @param index the index at which the specified view is to be removed
+ * @return this {@code AppEntitiesHeaderController} object
+ */
+ public AppEntitiesHeaderController removeAppEntity(int index) {
+ mAppEntities[index] = null;
+ return this;
+ }
+
+ /**
+ * Clear all app entities in app entities header.
+ *
+ * @return this {@code AppEntitiesHeaderController} object
+ */
+ public AppEntitiesHeaderController clearAllAppEntities() {
+ for (int index = 0; index < MAXIMUM_APPS; index++) {
+ removeAppEntity(index);
+ }
+ return this;
+ }
+
+ /**
+ * Done mutating app entities header, rebinds everything.
+ */
+ public void apply() {
+ bindHeaderTitleView();
+ bindHeaderDetailsView();
+
+ // Rebind all apps view
+ for (int index = 0; index < MAXIMUM_APPS; index++) {
+ bindAppEntityView(index);
+ }
+ }
+
+ private void bindHeaderTitleView() {
+ CharSequence titleText = "";
+ try {
+ titleText = mContext.getText(mHeaderTitleRes);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource of header title can't not be found!", e);
+ }
+ mHeaderTitleView.setText(titleText);
+ mHeaderTitleView.setVisibility(
+ TextUtils.isEmpty(titleText) ? View.GONE : View.VISIBLE);
+ }
+
+ private void bindHeaderDetailsView() {
+ CharSequence detailsText = "";
+ try {
+ detailsText = mContext.getText(mHeaderDetailsRes);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource of header details can't not be found!", e);
+ }
+ mHeaderDetailsView.setText(detailsText);
+ mHeaderDetailsView.setVisibility(
+ TextUtils.isEmpty(detailsText) ? View.GONE : View.VISIBLE);
+ mHeaderDetailsView.setOnClickListener(mDetailsOnClickListener);
+ }
+
+ private void bindAppEntityView(int index) {
+ final AppEntity appEntity = mAppEntities[index];
+ mAppEntityViews[index].setVisibility(appEntity != null ? View.VISIBLE : View.GONE);
+
+ if (appEntity != null) {
+ mAppIconViews[index].setImageDrawable(appEntity.icon);
+
+ mAppTitleViews[index].setVisibility(
+ TextUtils.isEmpty(appEntity.title) ? View.INVISIBLE : View.VISIBLE);
+ mAppTitleViews[index].setText(appEntity.title);
+
+ mAppSummaryViews[index].setVisibility(
+ TextUtils.isEmpty(appEntity.summary) ? View.INVISIBLE : View.VISIBLE);
+ mAppSummaryViews[index].setText(appEntity.summary);
+ }
+ }
+
+ private static class AppEntity {
+ public final Drawable icon;
+ public final CharSequence title;
+ public final CharSequence summary;
+
+ AppEntity(Drawable appIcon, CharSequence appTitle, CharSequence appSummary) {
+ icon = appIcon;
+ title = appTitle;
+ summary = appSummary;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
index ec8e9561ea83..2387b01d341d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -15,10 +15,9 @@
*/
package com.android.settingslib.applications;
-import android.annotation.NonNull;
import android.content.Context;
-import android.content.pm.permission.RuntimePermissionPresentationInfo;
-import android.content.pm.permission.RuntimePermissionPresenter;
+import android.permission.RuntimePermissionPresentationInfo;
+import android.permission.RuntimePermissionPresenter;
import java.text.Collator;
import java.util.ArrayList;
@@ -31,37 +30,33 @@ public class PermissionsSummaryHelper {
final PermissionsResultCallback callback) {
final RuntimePermissionPresenter presenter =
RuntimePermissionPresenter.getInstance(context);
- presenter.getAppPermissions(pkg, new RuntimePermissionPresenter.OnResultCallback() {
- @Override
- public void onGetAppPermissions(
- @NonNull List<RuntimePermissionPresentationInfo> permissions) {
- final int permissionCount = permissions.size();
+ presenter.getAppPermissions(pkg, permissions -> {
+ final int permissionCount = permissions.size();
- int grantedStandardCount = 0;
- int grantedAdditionalCount = 0;
- int requestedCount = 0;
- List<CharSequence> grantedStandardLabels = new ArrayList<>();
+ int grantedStandardCount = 0;
+ int grantedAdditionalCount = 0;
+ int requestedCount = 0;
+ List<CharSequence> grantedStandardLabels = new ArrayList<>();
- for (int i = 0; i < permissionCount; i++) {
- RuntimePermissionPresentationInfo permission = permissions.get(i);
- requestedCount++;
- if (permission.isGranted()) {
- if (permission.isStandard()) {
- grantedStandardLabels.add(permission.getLabel());
- grantedStandardCount++;
- } else {
- grantedAdditionalCount++;
- }
+ for (int i = 0; i < permissionCount; i++) {
+ RuntimePermissionPresentationInfo permission = permissions.get(i);
+ requestedCount++;
+ if (permission.isGranted()) {
+ if (permission.isStandard()) {
+ grantedStandardLabels.add(permission.getLabel());
+ grantedStandardCount++;
+ } else {
+ grantedAdditionalCount++;
}
}
+ }
- Collator collator = Collator.getInstance();
- collator.setStrength(Collator.PRIMARY);
- Collections.sort(grantedStandardLabels, collator);
+ Collator collator = Collator.getInstance();
+ collator.setStrength(Collator.PRIMARY);
+ Collections.sort(grantedStandardLabels, collator);
- callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
- grantedAdditionalCount, grantedStandardLabels);
- }
+ callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
+ grantedAdditionalCount, grantedStandardLabels);
}, null);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
new file mode 100644
index 000000000000..c3bc8da89685
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppEntitiesHeaderControllerTest {
+
+ private static final CharSequence TITLE = "APP_TITLE";
+ private static final CharSequence SUMMARY = "APP_SUMMARY";
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private Context mContext;
+ private Drawable mIcon;
+ private View mAppEntitiesHeaderView;
+ private AppEntitiesHeaderController mController;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate(
+ R.layout.app_entities_header, null /* root */);
+ mIcon = mContext.getDrawable(R.drawable.ic_menu);
+ mController = AppEntitiesHeaderController.newInstance(mContext,
+ mAppEntitiesHeaderView);
+ }
+
+ @Test
+ public void assert_amountOfMaximumAppsAreThree() {
+ assertThat(AppEntitiesHeaderController.MAXIMUM_APPS).isEqualTo(3);
+ }
+
+ @Test
+ public void setHeaderTitleRes_setTextRes_shouldSetToTitleView() {
+ mController.setHeaderTitleRes(R.string.expand_button_title).apply();
+ final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_title);
+
+ assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+ }
+
+ @Test
+ public void setHeaderDetailsRes_setTextRes_shouldSetToDetailsView() {
+ mController.setHeaderDetailsRes(R.string.expand_button_title).apply();
+ final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+ assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+ }
+
+ @Test
+ public void setHeaderDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
+ mController.setHeaderDetailsClickListener(v -> {
+ }).apply();
+ final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+ assertThat(view.hasOnClickListeners()).isTrue();
+ }
+
+ @Test
+ public void setAppEntity_indexLessThanZero_shouldThrowArrayIndexOutOfBoundsException() {
+ thrown.expect(ArrayIndexOutOfBoundsException.class);
+
+ mController.setAppEntity(-1, mIcon, TITLE, SUMMARY);
+ }
+
+ @Test
+ public void asetAppEntity_indexGreaterThanMaximum_shouldThrowArrayIndexOutOfBoundsException() {
+ thrown.expect(ArrayIndexOutOfBoundsException.class);
+
+ mController.setAppEntity(AppEntitiesHeaderController.MAXIMUM_APPS + 1, mIcon, TITLE,
+ SUMMARY);
+ }
+
+ @Test
+ public void setAppEntity_addAppToIndex0_shouldShowAppView1() {
+ mController.setAppEntity(0, mIcon, TITLE, SUMMARY).apply();
+ final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+ final ImageView appIconView = app1View.findViewById(R.id.app_icon);
+ final TextView appTitle = app1View.findViewById(R.id.app_title);
+ final TextView appSummary = app1View.findViewById(R.id.app_summary);
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(appIconView.getDrawable()).isNotNull();
+ assertThat(appTitle.getText()).isEqualTo(TITLE);
+ assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void setAppEntity_addAppToIndex1_shouldShowAppView2() {
+ mController.setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
+ final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+ final ImageView appIconView = app2View.findViewById(R.id.app_icon);
+ final TextView appTitle = app2View.findViewById(R.id.app_title);
+ final TextView appSummary = app2View.findViewById(R.id.app_summary);
+
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(appIconView.getDrawable()).isNotNull();
+ assertThat(appTitle.getText()).isEqualTo(TITLE);
+ assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void setAppEntity_addAppToIndex2_shouldShowAppView3() {
+ mController.setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
+ final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
+ final ImageView appIconView = app3View.findViewById(R.id.app_icon);
+ final TextView appTitle = app3View.findViewById(R.id.app_title);
+ final TextView appSummary = app3View.findViewById(R.id.app_summary);
+
+ assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(appIconView.getDrawable()).isNotNull();
+ assertThat(appTitle.getText()).isEqualTo(TITLE);
+ assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+ }
+
+ @Test
+ public void removeAppEntity_removeIndex0_shouldNotShowAppView1() {
+ mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
+ .setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
+ final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+ final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mController.removeAppEntity(0).apply();
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void clearAllAppEntities_shouldNotShowAllAppViews() {
+ mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
+ .setAppEntity(1, mIcon, TITLE, SUMMARY)
+ .setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
+ final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+ final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+ final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
+
+ assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mController.clearAllAppEntities().apply();
+ assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
+ assertThat(app2View.getVisibility()).isEqualTo(View.GONE);
+ assertThat(app3View.getVisibility()).isEqualTo(View.GONE);
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 424368d2600c..b071355986f5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -418,7 +418,7 @@ public class SettingsProvider extends ContentProvider {
case Settings.CALL_METHOD_PUT_CONFIG: {
String value = getSettingValue(args);
final boolean makeDefault = getSettingMakeDefault(args);
- insertConfigSetting(name, value, null, makeDefault, requestingUserId, false);
+ insertConfigSetting(name, value, makeDefault);
break;
}
@@ -447,7 +447,7 @@ public class SettingsProvider extends ContentProvider {
case Settings.CALL_METHOD_RESET_CONFIG: {
final int mode = getResetModeEnforcingPermission(args);
String prefix = getSettingPrefix(args);
- resetConfigSetting(requestingUserId, mode, prefix);
+ resetConfigSetting(mode, prefix);
break;
}
@@ -466,7 +466,7 @@ public class SettingsProvider extends ContentProvider {
}
case Settings.CALL_METHOD_DELETE_CONFIG: {
- int rows = deleteConfigSetting(name, requestingUserId, false) ? 1 : 0;
+ int rows = deleteConfigSetting(name) ? 1 : 0;
Bundle result = new Bundle();
result.putInt(RESULT_ROWS_DELETED, rows);
return result;
@@ -1067,38 +1067,33 @@ public class SettingsProvider extends ContentProvider {
}
}
- private boolean insertConfigSetting(String name, String value, String tag,
- boolean makeDefault, int requestingUserId, boolean forceNotify) {
+ private boolean insertConfigSetting(String name, String value, boolean makeDefault) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertConfigSetting(" + name + ", " + value + ", "
- + ", " + tag + ", " + makeDefault + ", " + requestingUserId
- + ", " + forceNotify + ")");
+ + makeDefault + ")");
}
- return mutateConfigSetting(name, value, tag, makeDefault, requestingUserId,
- MUTATION_OPERATION_INSERT, forceNotify, 0);
+ return mutateConfigSetting(name, value, null, makeDefault,
+ MUTATION_OPERATION_INSERT, 0);
}
- private boolean deleteConfigSetting(String name, int requestingUserId, boolean forceNotify) {
+ private boolean deleteConfigSetting(String name) {
if (DEBUG) {
- Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ", " + requestingUserId
- + ", " + forceNotify + ")");
+ Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ")");
}
- return mutateConfigSetting(name, null, null, false, requestingUserId,
- MUTATION_OPERATION_DELETE, forceNotify, 0);
+ return mutateConfigSetting(name, null, null, false,
+ MUTATION_OPERATION_DELETE, 0);
}
- private void resetConfigSetting(int requestingUserId, int mode, String prefix) {
+ private void resetConfigSetting(int mode, String prefix) {
if (DEBUG) {
- Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
- + mode + ", " + prefix + ")");
+ Slog.v(LOG_TAG, "resetConfigSetting(" + mode + ", " + prefix + ")");
}
- mutateConfigSetting(null, null, prefix, false, requestingUserId,
- MUTATION_OPERATION_RESET, false, mode);
+ mutateConfigSetting(null, null, prefix, false,
+ MUTATION_OPERATION_RESET, mode);
}
private boolean mutateConfigSetting(String name, String value, String prefix,
- boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
- int mode) {
+ boolean makeDefault, int operation, int mode) {
// TODO(b/117663715): check the new permission when it's added.
// enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -1107,13 +1102,13 @@ public class SettingsProvider extends ContentProvider {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
- UserHandle.USER_SYSTEM, name, value, null, makeDefault,
- getCallingPackage(), forceNotify, null);
+ UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
+ getCallingPackage(), false, null);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
- UserHandle.USER_SYSTEM, name, forceNotify, null);
+ UserHandle.USER_SYSTEM, name, false, null);
}
case MUTATION_OPERATION_RESET: {
@@ -2631,13 +2626,20 @@ public class SettingsProvider extends ContentProvider {
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, String packageName, boolean forceNotify,
Set<String> criticalSettings) {
+ return insertSettingLocked(type, userId, name, value, tag, makeDefault, false,
+ packageName, forceNotify, criticalSettings);
+ }
+
+ public boolean insertSettingLocked(int type, int userId, String name, String value,
+ String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
+ boolean forceNotify, Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.insertSettingLocked(name, value,
- tag, makeDefault, packageName);
+ tag, makeDefault, forceNonSystemPackage, packageName);
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index ae2ca3f87ae7..521163f50ca1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -377,6 +377,13 @@ final class SettingsState {
@GuardedBy("mLock")
public boolean insertSettingLocked(String name, String value, String tag,
boolean makeDefault, String packageName) {
+ return insertSettingLocked(name, value, tag, makeDefault, false, packageName);
+ }
+
+ // The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
+ public boolean insertSettingLocked(String name, String value, String tag,
+ boolean makeDefault, boolean forceNonSystemPackage, String packageName) {
if (TextUtils.isEmpty(name)) {
return false;
}
@@ -387,7 +394,7 @@ final class SettingsState {
Setting newState;
if (oldState != null) {
- if (!oldState.update(value, makeDefault, packageName, tag, false)) {
+ if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) {
return false;
}
newState = oldState;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
index 59de6a7e64b9..9d0462e14b63 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
@@ -17,10 +17,9 @@
package com.android.providers.settings;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
-import static org.junit.Assert.assertNotNull;
-
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
@@ -147,10 +146,10 @@ public class DeviceConfigServiceTest {
}
@Test
- public void testReset_setUntrustedDefault() throws Exception {
+ public void testReset() throws Exception {
String newValue = "value2";
- // make sValue the untrusted default (set by root)
+ // make sValue the default value
executeShellCommand(
"device_config put " + sNamespace + " " + sKey + " " + sValue + " default");
// make newValue the current value
@@ -159,40 +158,19 @@ public class DeviceConfigServiceTest {
String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
assertEquals(newValue, result);
+ // reset values that were set by untrusted packages
executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // back to the default
+ // the default value has been restored
assertEquals(sValue, result);
+ // clear values that were set by untrusted packages
executeShellCommand("device_config reset trusted_defaults " + sNamespace);
result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // not trusted default was set
+ // even the default value is gone now
assertNull(result);
}
- @Test
- public void testReset_setTrustedDefault() throws Exception {
- String newValue = "value2";
-
- // make sValue the trusted default (set by system)
- putWithContentProvider(mContentResolver, sNamespace, sKey, sValue, true);
- // make newValue the current value
- executeShellCommand(
- "device_config put " + sNamespace + " " + sKey + " " + newValue);
- String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- assertEquals(newValue, result);
-
- executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
- result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // back to the default
- assertEquals(sValue, result);
-
- executeShellCommand("device_config reset trusted_defaults " + sNamespace);
- result = getFromContentProvider(mContentResolver, sNamespace, sKey);
- // our trusted default is still set
- assertEquals(sValue, result);
- }
-
private static void executeShellCommand(String command) throws IOException {
InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation()
.getUiAutomation().executeShellCommand(command).getFileDescriptor());
@@ -212,8 +190,7 @@ public class DeviceConfigServiceTest {
if (makeDefault) {
args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- resolver.call(
- CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
+ resolver.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
}
private static String getFromContentProvider(ContentResolver resolver, String namespace,
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index dc6a6126bc5d..9595e28eb6e2 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -28,7 +28,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तुमचा बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अ‍ॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अ‍ॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
new file mode 100644
index 000000000000..b2307e71e3ce
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.systemui.bubbles.BubbleExpandedViewContainer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/bubble_expanded_view">
+
+ <!-- TODO: header -->
+
+ <View
+ android:id="@+id/pointer_view"
+ android:layout_width="@dimen/bubble_pointer_width"
+ android:layout_height="@dimen/bubble_pointer_height"
+ />
+</com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7ea4027fc7f1..6fb44ccf0e07 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -89,7 +89,7 @@
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"कॅमेरा म्हणून माउंट करा (PTP)"</string>
- <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac साठी Android फाईल स्थानांतर अॅप इंस्टॉल करा"</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac साठी Android फाईल स्थानांतर अ‍ॅप इंस्टॉल करा"</string>
<string name="accessibility_back" msgid="567011538994429120">"मागे"</string>
<string name="accessibility_home" msgid="8217216074895377641">"होम"</string>
<string name="accessibility_menu" msgid="316839303324695949">"मेनू"</string>
@@ -598,7 +598,7 @@
<string name="tuner_full_importance_settings" msgid="3207312268609236827">"पॉवर सूचना नियंत्रणे"</string>
<string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"चालू"</string>
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
- <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
+ <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अ‍ॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अ‍ॅपमधील सर्व सूचना ब्लॉक करा"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"आता तुम्हाला या सूचना दिसणार नाहीत"</string>
<string name="notification_channel_minimized" msgid="1664411570378910931">"या सूचना लहान केल्या जातील"</string>
@@ -621,13 +621,13 @@
<string name="inline_keep_showing_app" msgid="1723113469580031041">"या अ‍ॅपकडील सूचना दाखवणे सुरू ठेवायचे?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"या सूचना बंद करता येत नाहीत"</string>
<string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> मार्गे"</string>
- <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string>
- <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string>
- <string name="appops_overlay" msgid="6165912637560323464">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string>
- <string name="appops_camera_mic" msgid="1576901651150187433">"हे अॅप मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
- <string name="appops_camera_overlay" msgid="8869400080809298814">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि कॅमेरा वापरत आहे."</string>
- <string name="appops_mic_overlay" msgid="4835157962857919804">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन वापरत आहे."</string>
- <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"हे अॅप तुमच्या स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_camera" msgid="8100147441602585776">"हे अ‍ॅप कॅमेरा वापरत आहे."</string>
+ <string name="appops_microphone" msgid="741508267659494555">"हे अ‍ॅप मायक्रोफोन वापरत आहे."</string>
+ <string name="appops_overlay" msgid="6165912637560323464">"हे अ‍ॅप स्क्रीनवरील इतर अ‍ॅप्स वर प्रदर्शित होत आहे."</string>
+ <string name="appops_camera_mic" msgid="1576901651150187433">"हे अ‍ॅप मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_camera_overlay" msgid="8869400080809298814">"हे अ‍ॅप तुमच्या स्क्रीनवरील इतर अ‍ॅप्स वर प्रदर्शित होत आहे आणि कॅमेरा वापरत आहे."</string>
+ <string name="appops_mic_overlay" msgid="4835157962857919804">"हे अ‍ॅप तुमच्या स्क्रीनवरील इतर अ‍ॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन वापरत आहे."</string>
+ <string name="appops_camera_mic_overlay" msgid="6718768197048030993">"हे अ‍ॅप तुमच्या स्क्रीनवरील इतर अ‍ॅप्स वर प्रदर्शित होत आहे आणि मायक्रोफोन आणि कॅमेरा वापरत आहे."</string>
<string name="notification_appops_settings" msgid="1028328314935908050">"सेटिंग्ज"</string>
<string name="notification_appops_ok" msgid="1156966426011011434">"ओके"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string>
@@ -776,8 +776,8 @@
<string name="accessibility_qs_edit_tile_move" msgid="3108103090006972938">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर हलवा"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"द्रुत सेटिंग्ज संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
- <string name="dock_forced_resizable" msgid="5914261505436217520">"अॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
+ <string name="dock_forced_resizable" msgid="5914261505436217520">"अ‍ॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="4230857851756391925">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"दुसऱ्या डिस्प्लेवर अ‍ॅप लाँच होणार नाही."</string>
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"सेटिंग्ज उघडा."</string>
@@ -806,7 +806,7 @@
<string name="pip_skip_to_prev" msgid="1955311326688637914">"डावलून मागे जा"</string>
<string name="thermal_shutdown_title" msgid="4458304833443861111">"तापल्‍यामुळे फोन बंद झाला"</string>
<string name="thermal_shutdown_message" msgid="9006456746902370523">"तुमचा फोन आता व्‍यवस्थित चालू आहे"</string>
- <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्‍यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n •मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्‍यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अ‍ॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अ‍ॅप यासारखे)\n •मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
<string name="high_temp_title" msgid="4589508026407318374">"फोन ऊष्ण होत आहे"</string>
<string name="high_temp_notif_message" msgid="5642466103153429279">"फोन थंड होत असताना काही वैशिष्‍ट्ये मर्यादित असतात"</string>
<string name="high_temp_dialog_message" msgid="6840700639374113553">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
@@ -832,9 +832,9 @@
<string name="notification_channel_hints" msgid="7323870212489152689">"सूचना"</string>
<string name="instant_apps" msgid="6647570248119804907">"इन्सटंट अ‍ॅप्स"</string>
<string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> रन होत आहे"</string>
- <string name="instant_apps_message" msgid="1183313016396018086">"इंस्टॉल केल्याशिवाय अॅप उघडले."</string>
- <string name="instant_apps_message_with_help" msgid="6179830437630729747">"इंस्टॉल केल्याशिवाय अॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
- <string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
+ <string name="instant_apps_message" msgid="1183313016396018086">"इंस्टॉल केल्याशिवाय अ‍ॅप उघडले."</string>
+ <string name="instant_apps_message_with_help" msgid="6179830437630729747">"इंस्टॉल केल्याशिवाय अ‍ॅप उघडले. अधिक जाणून घेण्यासाठी टॅप करा."</string>
+ <string name="app_info" msgid="6856026610594615344">"अ‍ॅप माहिती"</string>
<string name="go_to_web" msgid="2650669128861626071">"ब्राउझरवर जा"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string>
@@ -873,7 +873,7 @@
<!-- no translation found for ongoing_privacy_chip_content_multiple_apps_single_op (4871926099254314088) -->
<string name="ongoing_privacy_dialog_cancel" msgid="5479124524931216790">"रद्द करा"</string>
<string name="ongoing_privacy_dialog_open_settings" msgid="2074844974365194279">"तपशील पाहा"</string>
- <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"अॅप तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे"</string>
+ <string name="ongoing_privacy_dialog_single_app_title" msgid="6019646962021696632">"अ‍ॅप तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे"</string>
<string name="ongoing_privacy_dialog_multiple_apps_title" msgid="8013356222977903365">"अॅप्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहेत"</string>
<string name="ongoing_privacy_dialog_separator" msgid="6854860652480837439">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="2400503446627122483">" आणि "</string>
@@ -882,6 +882,6 @@
<string name="privacy_type_microphone" msgid="4153045784928554506">"मायक्रोफोन"</string>
<plurals name="ongoing_privacy_dialog_overflow_text" formatted="false" msgid="3441296594927649172">
<item quantity="one">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅ​प</item>
- <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अॅप्स</item>
+ <item quantity="other">इतर <xliff:g id="NUM_APPS_1">%d</xliff:g> अ‍ॅप्स</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 4f5b23e2b546..558fd9b7382d 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -785,7 +785,7 @@
<string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Lukk hurtiginnstillingene."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Alarm er angitt."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Logget på som <xliff:g id="ID_1">%s</xliff:g>"</string>
- <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen Internett-tilkobling"</string>
+ <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen internettilkobling"</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Åpne informasjonen."</string>
<string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Utilgjengelig fordi <xliff:g id="REASON">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5e7a1198b962..06467b85a687 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -651,7 +651,7 @@
<item quantity="other">%d minuten</item>
<item quantity="one">%d minuut</item>
</plurals>
- <string name="battery_panel_title" msgid="7944156115535366613">"Accugebruik"</string>
+ <string name="battery_panel_title" msgid="7944156115535366613">"Batterijgebruik"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterijbesparing niet beschikbaar tijdens opladen"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterijbesparing"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vermindert de prestaties en achtergrondgegevens"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b6c9b8c8cee8..3851190fdeec 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -361,6 +361,7 @@
<!-- The height of the qs customize header. Should be (28dp - qs_tile_margin_top_bottom). -->
<dimen name="qs_customize_header_min_height">40dp</dimen>
<dimen name="qs_tile_margin_top">18dp</dimen>
+ <dimen name="qs_tile_background_size">40dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<!-- Maximum width of quick quick settings panel. Defaults to MATCH_PARENT-->
<dimen name="qs_quick_layout_width">-1px</dimen>
@@ -993,4 +994,10 @@
<dimen name="bubble_icon_size">24dp</dimen>
<!-- Default height of the expanded view shown when the bubble is expanded -->
<dimen name="bubble_expanded_default_height">400dp</dimen>
+ <!-- Height of the triangle that points to the expanded bubble -->
+ <dimen name="bubble_pointer_height">4dp</dimen>
+ <!-- Width of the triangle that points to the expanded bubble -->
+ <dimen name="bubble_pointer_width">6dp</dimen>
+ <!-- Extra padding around the dismiss target for bubbles -->
+ <dimen name="bubble_dismiss_slop">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index a9cf857c3e50..807edf624db8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -25,6 +25,8 @@ import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.ViewRootImpl;
+import java.util.function.Consumer;
+
/**
* Helper class to apply surface transactions in sync with RenderThread.
*/
@@ -78,6 +80,35 @@ public class SyncRtSurfaceTransactionApplier {
applyParams(t.mTransaction, params, t.mTmpValues);
}
+ /**
+ * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * attached if necessary.
+ */
+ public static void create(final View targetView,
+ final Consumer<SyncRtSurfaceTransactionApplier> callback) {
+ if (targetView == null) {
+ // No target view, no applier
+ callback.accept(null);
+ } else if (targetView.getViewRootImpl() != null) {
+ // Already attached, we're good to go
+ callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
+ } else {
+ // Haven't been attached before we can get the view root
+ targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ targetView.removeOnAttachStateChangeListener(this);
+ callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ // Do nothing
+ }
+ });
+ }
+ }
+
private static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
t.setMatrix(params.surface, params.matrix, tmpFloat9);
t.setWindowCrop(params.surface, params.windowCrop);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
new file mode 100644
index 000000000000..e28d96b2def9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.ShapeDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Container for the expanded bubble view, handles rendering the caret and header of the view.
+ */
+public class BubbleExpandedViewContainer extends LinearLayout {
+
+ // The triangle pointing to the expanded view
+ private View mPointerView;
+ // The view that is being displayed for the expanded state
+ private View mExpandedView;
+
+ public BubbleExpandedViewContainer(Context context) {
+ this(context, null);
+ }
+
+ public BubbleExpandedViewContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setOrientation(VERTICAL);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ Resources res = getResources();
+ mPointerView = findViewById(R.id.pointer_view);
+ int width = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
+ int height = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
+ ShapeDrawable triangleDrawable = new ShapeDrawable(
+ TriangleShape.create(width, height, true /* pointUp */));
+ triangleDrawable.setTint(Color.WHITE); // TODO: dark mode
+ mPointerView.setBackground(triangleDrawable);
+ }
+
+ /**
+ * Set the x position that the tip of the triangle should point to.
+ */
+ public void setPointerPosition(int x) {
+ // Adjust for the pointer size
+ x -= (mPointerView.getWidth() / 2);
+ mPointerView.setTranslationX(x);
+ }
+
+ /**
+ * Set the view to display for the expanded state. Passing null will clear the view.
+ */
+ public void setExpandedView(View view) {
+ if (mExpandedView != null) {
+ removeView(mExpandedView);
+ }
+ mExpandedView = view;
+ if (mExpandedView != null) {
+ addView(mExpandedView);
+ }
+ }
+
+ /**
+ * @return the view containing the expanded content, can be null.
+ */
+ @Nullable
+ public View getExpandedView() {
+ return mExpandedView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e395c4c2765c..dfd18b23a5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.RectF;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -52,7 +53,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
private Point mDisplaySize;
private FrameLayout mBubbleContainer;
- private FrameLayout mExpandedViewContainer;
+ private BubbleExpandedViewContainer mExpandedViewContainer;
private int mBubbleSize;
private int mBubblePadding;
@@ -111,7 +112,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
int padding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
- mExpandedViewContainer = new FrameLayout(context);
+ mExpandedViewContainer = (BubbleExpandedViewContainer)
+ LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view,
+ this /* parent */, false /* attachToRoot */);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setPadding(padding, padding, padding, padding);
mExpandedViewContainer.setClipChildren(false);
@@ -390,16 +393,19 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
ExpandableNotificationRow row = mExpandedBubble.getRowView();
if (!row.equals(mExpandedViewContainer.getChildAt(0))) {
// Different expanded view than what we have
- mExpandedViewContainer.removeAllViews();
+ mExpandedViewContainer.setExpandedView(null);
}
- mExpandedViewContainer.addView(row);
+ int pointerPosition = mExpandedBubble.getPosition().x
+ + (mExpandedBubble.getWidth() / 2);
+ mExpandedViewContainer.setPointerPosition(pointerPosition);
+ mExpandedViewContainer.setExpandedView(row);
}
}
private void applyCurrentState() {
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (!mIsExpanded) {
- mExpandedViewContainer.removeAllViews();
+ mExpandedViewContainer.setExpandedView(null);
} else {
mExpandedViewContainer.setTranslationY(mBubbleContainer.getHeight());
ExpandableNotificationRow row = mExpandedBubble.getRowView();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 8262b54f3cd6..8224365e7b96 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -51,6 +51,8 @@ public class PipDismissViewController {
// Used for dismissing a bubble -- bubble should be in the target to be considered a dismiss
private View mTargetView;
+ private int mTargetSlop;
+ private Point mWindowSize;
private int[] mLoc = new int[2];
private boolean mIntersecting;
private Vibrator mVibe;
@@ -69,12 +71,14 @@ public class PipDismissViewController {
// Determine sizes for the view
final Rect stableInsets = new Rect();
WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
- final Point windowSize = new Point();
- mWindowManager.getDefaultDisplay().getRealSize(windowSize);
+ mWindowSize = new Point();
+ mWindowManager.getDefaultDisplay().getRealSize(mWindowSize);
final int gradientHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.pip_dismiss_gradient_height);
final int bottomMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.pip_dismiss_text_bottom_margin);
+ mTargetSlop = mContext.getResources().getDimensionPixelSize(
+ R.dimen.bubble_dismiss_slop);
// Create a new view for the dismiss target
LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -96,7 +100,7 @@ public class PipDismissViewController {
// Add the target to the window
LayoutParams lp = new LayoutParams(
LayoutParams.MATCH_PARENT, gradientHeight,
- 0, windowSize.y - gradientHeight,
+ 0, mWindowSize.y - gradientHeight,
LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
LayoutParams.FLAG_LAYOUT_IN_SCREEN
| LayoutParams.FLAG_NOT_TOUCHABLE
@@ -112,7 +116,7 @@ public class PipDismissViewController {
/**
- * Updates the dismiss target based on location of the view.
+ * Updates the dismiss target based on location of the view, only used for bubbles not for PIP.
*
* @return whether the view is within the dismiss target.
*/
@@ -127,11 +131,13 @@ public class PipDismissViewController {
mTargetView.getLocationOnScreen(mLoc);
Rect targetRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + mTargetView.getWidth(),
mLoc[1] + mTargetView.getHeight());
+ expandRect(targetRect, mTargetSlop);
boolean intersecting = targetRect.intersect(viewRect);
- if (intersecting && !mIntersecting) {
+ if (intersecting != mIntersecting) {
// TODO: is this the right effect?
- mVibe.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
- mIntersecting = true;
+ mVibe.vibrate(VibrationEffect.get(intersecting
+ ? VibrationEffect.EFFECT_CLICK
+ : VibrationEffect.EFFECT_TICK));
}
mIntersecting = intersecting;
return intersecting;
@@ -139,7 +145,6 @@ public class PipDismissViewController {
return false;
}
-
/**
* Shows the dismiss target.
*/
@@ -172,4 +177,11 @@ public class PipDismissViewController {
.start();
}
}
+
+ private void expandRect(Rect outRect, int expandAmount) {
+ outRect.left = Math.max(0, outRect.left - expandAmount);
+ outRect.top = Math.max(0, outRect.top - expandAmount);
+ outRect.right = Math.min(mWindowSize.x, outRect.right + expandAmount);
+ outRect.bottom = Math.min(mWindowSize.y, outRect.bottom + expandAmount);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 3a96595dee06..32fd2dcedd0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -19,14 +19,19 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
+import android.util.PathParser;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -46,6 +51,7 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState;
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private static final String TAG = "QSTileBaseView";
+ private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
private final H mHandler = new H();
private final int[] mLocInScreen = new int[2];
private final FrameLayout mIconFrame;
@@ -62,6 +68,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private final int mColorInactive;
private final int mColorDisabled;
private int mCircleColor;
+ private int mBgSize;
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
@@ -71,15 +78,23 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
super(context);
// Default to Quick Tile padding, and QSTileView will specify its own padding.
int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
-
mIconFrame = new FrameLayout(context);
mIconFrame.setForegroundGravity(Gravity.CENTER);
int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
addView(mIconFrame, new LayoutParams(size, size));
mBg = new ImageView(getContext());
+ Path path = new Path(PathParser.createPathFromPathData(
+ context.getResources().getString(ICON_MASK_ID)));
+ float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+ PathShape p = new PathShape(path, pathSize, pathSize);
+ ShapeDrawable d = new ShapeDrawable(p);
+ int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
+ d.setIntrinsicHeight(bgSize);
+ d.setIntrinsicWidth(bgSize);
mBg.setScaleType(ScaleType.FIT_CENTER);
- mBg.setImageResource(R.drawable.ic_qs_circle);
- mIconFrame.addView(mBg);
+ mBg.setImageDrawable(d);
+ mIconFrame.addView(mBg, ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
mIcon = icon;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -107,7 +122,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
setFocusable(true);
}
- public View getBgCicle() {
+ public View getBgCircle() {
return mBg;
}
@@ -303,6 +318,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private class H extends Handler {
private static final int STATE_CHANGED = 1;
+
public H() {
super(Looper.getMainLooper());
}
@@ -314,4 +330,4 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
}
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index d9fe98257d4c..f543b4622611 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -588,7 +588,7 @@ public class NotificationData {
}
public boolean areGutsExposed() {
- return row != null && row.getGuts().isExposed();
+ return row != null && row.getGuts() != null && row.getGuts().isExposed();
}
public boolean isChildInGroup() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 20c48163e6b1..b1fa6a531438 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -60,10 +59,11 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
private ViewGroup mTransientContainer;
private boolean mInShelf;
private boolean mTransformingInShelf;
- @Nullable private ExpandableViewState mViewState;
+ private final ExpandableViewState mViewState;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mViewState = createExpandableViewState();
}
@Override
@@ -536,11 +536,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
/** Sets {@link ExpandableViewState} to default state. */
public ExpandableViewState resetViewState() {
- // TODO(http://b/119762423): Move the null check to getViewState().
- if (mViewState == null) {
- mViewState = createExpandableViewState();
- }
-
// initialize with the default values of the view
mViewState.height = getIntrinsicHeight();
mViewState.gone = getVisibility() == View.GONE;
@@ -573,9 +568,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
/** Applies internal {@link ExpandableViewState} to this view. */
public void applyViewState() {
- if (mViewState == null) {
- Log.wtf(TAG, "No child state was found when applying this state to the hostView");
- } else if (!mViewState.gone) {
+ if (!mViewState.gone) {
mViewState.applyToView(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 989fb211f8d2..d97162c2ef1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -47,6 +48,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
ExpandableNotificationRow.LayoutListener {
@@ -74,6 +76,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
private MenuItem mSnoozeItem;
private ArrayList<MenuItem> mLeftMenuItems;
private ArrayList<MenuItem> mRightMenuItems;
+ private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
private ValueAnimator mFadeAnimator;
@@ -287,6 +290,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
private void populateMenuViews() {
if (mMenuContainer != null) {
mMenuContainer.removeAllViews();
+ mMenuItemsByView.clear();
} else {
mMenuContainer = new FrameLayout(mContext);
}
@@ -486,10 +490,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
final int centerY = v.getHeight() / 2;
final int x = mIconLocation[0] - mParentLocation[0] + centerX;
final int y = mIconLocation[1] - mParentLocation[1] + centerY;
- final int index = mMenuContainer.indexOfChild(v);
- if (mMenuListener != null) {
- mMenuListener.onMenuClicked(mParent, x, y,
- (mOnLeft ? mLeftMenuItems : mRightMenuItems).get(index));
+ if (mMenuItemsByView.containsKey(v)) {
+ mMenuListener.onMenuClicked(mParent, x, y, mMenuItemsByView.get(v));
}
}
@@ -665,6 +667,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
lp.height = mHorizSpaceForIcon;
menuView.setLayoutParams(lp);
}
+ mMenuItemsByView.put(menuView, item);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 9eb5737ad00d..0cec6371d813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -296,13 +296,15 @@ public class QuickStepController implements GestureHelper {
} else if (exceededSwipeHorizontalTouchSlop) {
if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
// Swiping left (rtl) gesture
- int index = isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive)
+ int index = mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] != null
+ && isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive)
? ACTION_SWIPE_LEFT_FROM_EDGE_INDEX : ACTION_SWIPE_LEFT_INDEX;
tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
event);
} else {
// Swiping right (ltr) gesture
- int index = isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive)
+ int index = mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] != null
+ && isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive)
? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX : ACTION_SWIPE_RIGHT_INDEX;
tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b351f1d9f150..1d6a1e81246c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -121,8 +121,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.widget.MessagingGroup;
-import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
@@ -1116,8 +1114,6 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onDensityOrFontScaleChanged() {
- MessagingMessage.dropCache();
- MessagingGroup.dropCache();
// TODO: Remove this.
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index d99af1fb57bf..acdd5e995322 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -37,6 +37,8 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.MessagingGroup;
+import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
@@ -62,9 +64,11 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-public class StatusBarNotificationPresenter implements NotificationPresenter {
+public class StatusBarNotificationPresenter implements NotificationPresenter,
+ ConfigurationController.ConfigurationListener {
private final LockscreenGestureLogger mLockscreenGestureLogger =
Dependency.get(LockscreenGestureLogger.class);
@@ -168,9 +172,13 @@ public class StatusBarNotificationPresenter implements NotificationPresenter {
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
+ @Override
public void onDensityOrFontScaleChanged() {
+ MessagingMessage.dropCache();
+ MessagingGroup.dropCache();
if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index abb8c7939e63..2805908d114a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -601,6 +601,25 @@ public class QuickStepControllerTest extends SysuiTestCase {
assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
}
+ @Test
+ public void testNoEdgeActionsTriggerNormalActions() {
+ doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+ doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
+
+ NavigationGestureAction swipeUp = mockAction(true);
+ NavigationGestureAction swipeDown = mockAction(true);
+ NavigationGestureAction swipeLeft = mockAction(true);
+ NavigationGestureAction swipeRight = mockAction(true);
+ mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
+ null /* swipeLeftFromEdgeAction */,
+ null /* swipeLeftFromEdgeAction */);
+
+ // Swipe Left from Edge
+ assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH, 1, 5, 1);
+ // Swipe Right from Edge
+ assertGestureTriggersAction(swipeRight, 0, 1, NAVBAR_WIDTH, 5);
+ }
+
private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
int navPos) {
mController.setBarState(isRTL, navPos);
@@ -665,7 +684,6 @@ public class QuickStepControllerTest extends SysuiTestCase {
reset(buttonView);
}
-
private MotionEvent event(int action, float x, float y) {
final MotionEvent event = mock(MotionEvent.class);
doReturn(x).when(event).getX();
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index dec0c291fb22..2d3064017719 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6621,6 +6621,22 @@ message MetricsEvent {
// OS: Q
FIELD_IS_FULL_QS = 1593;
+ // ACTION: Display folding state was changed
+ // CATEGORY: OTHER
+ // SUBTYPE: 1 if display is folded, 0 if not.
+ // OS: Q
+ ACTION_DISPLAY_FOLD = 1594;
+
+ // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_WIFI_DPP_CONFIGURATOR = 1595;
+
+ // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_WIFI_DPP_ENROLLEE = 1596;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index fba639c3fc4a..121267675329 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -216,6 +216,10 @@ message SystemMessage {
// Package: android
NOTE_SOFTAP_CONFIG_CHANGED = 50;
+ // Notify the user that connected to app suggested network.
+ // Package: android
+ NOTE_CONNECTED_TO_NETWORK_SUGGESTION = 51;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 1fda07499a33..e9ce737eb5a5 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1344,6 +1344,27 @@ message WifiPowerStats {
// Amount of time wifi is in tx (ms)
optional int64 tx_time_ms = 5;
+
+ // Amount of time kernel is active because of wifi data (ms)
+ optional int64 wifi_kernel_active_time_ms = 6;
+
+ // Number of packets sent (tx)
+ optional int64 num_packets_tx = 7;
+
+ // Number of bytes sent (tx)
+ optional int64 num_bytes_tx = 8;
+
+ // Number of packets received (rx)
+ optional int64 num_packets_rx = 9;
+
+ // Number of bytes sent (rx)
+ optional int64 num_bytes_rx = 10;
+
+ // Amount of time wifi is in sleep (ms)
+ optional int64 sleep_time_ms = 11;
+
+ // Amount of time wifi is scanning (ms)
+ optional int64 scan_time_ms = 12;
}
// Metrics for Wifi Wake
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e8887e7a2ebe..612c9294f5d1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -484,15 +484,15 @@ public final class AutofillManagerService
}
// Called by Shell command.
- void getScore(@Nullable String algorithmName, @NonNull String value1,
+ void calculateScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
enforceCallingPermissionForManagement();
final FieldClassificationStrategy strategy =
new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
- strategy.getScores(callback, algorithmName, null,
- Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 });
+ strategy.calculateScores(callback, Arrays.asList(AutofillValue.forText(value1)),
+ new String[] { value2 }, null, algorithmName, null, null, null);
}
// Called by Shell command.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5a0d12cfd8e5..fa62ef829b32 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -76,7 +76,6 @@ import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractPerUserSystemService;
-import com.android.server.infra.AbstractRemoteService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.infra.SecureSettingsServiceNameResolver;
@@ -1031,8 +1030,7 @@ final class AutofillManagerServiceImpl
return null;
}
final ComponentName componentName = RemoteAugmentedAutofillService.getComponentName(
- getContext(), serviceName, mUserId,
- mAugmentedAutofillResolver.isTemporaryLocked());
+ serviceName, mUserId, mAugmentedAutofillResolver.isTemporaryLocked());
if (componentName == null) return null;
if (sVerbose) {
Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
@@ -1041,8 +1039,7 @@ final class AutofillManagerServiceImpl
mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
@Override
- public void onServiceDied(
- AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
+ public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
// TODO(b/111330312): properly implement
Slog.w(TAG, "remote augmented autofill service died");
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 35c51027df86..c562fb1b7dde 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -233,7 +233,7 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand {
final String value2 = getNextArgRequired();
final CountDownLatch latch = new CountDownLatch(1);
- mService.getScore(algorithm, value1, value2, new RemoteCallback((result) -> {
+ mService.calculateScore(algorithm, value1, value2, new RemoteCallback((result) -> {
final Scores scores = result.getParcelable(EXTRA_SCORES);
if (scores == null) {
pw.println("no score");
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 293f908e2708..9db6254a8baa 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -15,11 +15,12 @@
*/
package com.android.server.autofill;
-import static com.android.server.autofill.Helper.sDebug;
-import static com.android.server.autofill.Helper.sVerbose;
import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
+import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
+
import android.Manifest;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -40,6 +41,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.service.autofill.AutofillFieldClassificationService;
import android.service.autofill.IAutofillFieldClassificationService;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.autofill.AutofillValue;
@@ -257,13 +259,13 @@ final class FieldClassificationStrategy {
return parser.get(res, resourceId);
}
- //TODO(b/70291841): rename this method (and all others in the chain) to something like
- // calculateScores() ?
- void getScores(RemoteCallback callback, @Nullable String algorithmName,
- @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
- @NonNull String[] userDataValues) {
- connectAndRun((service) -> service.getScores(callback, algorithmName,
- algorithmArgs, actualValues, userDataValues));
+ void calculateScores(RemoteCallback callback, @NonNull List<AutofillValue> actualValues,
+ @NonNull String[] userDataValues, @NonNull String[] categoryIds,
+ @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
+ @Nullable ArrayMap<String, String> algorithms,
+ @Nullable ArrayMap<String, Bundle> args) {
+ connectAndRun((service) -> service.calculateScores(callback, actualValues,
+ userDataValues, categoryIds, defaultAlgorithm, defaultArgs, algorithms, args));
}
void dump(String prefix, PrintWriter pw) {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 222888cc8ce3..fc7265db1be2 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -26,7 +26,6 @@ import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.IInterface;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.augmented.AugmentedAutofillService;
@@ -43,7 +42,8 @@ import com.android.internal.os.IResultReceiver;
import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
final class RemoteAugmentedAutofillService
- extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService> {
+ extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService,
+ IAugmentedAutofillService> {
private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
@@ -51,20 +51,16 @@ final class RemoteAugmentedAutofillService
private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
- private final RemoteAugmentedAutofillServiceCallbacks mCallbacks;
- private IAugmentedAutofillService mService;
-
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
boolean bindInstantServiceAllowed, boolean verbose) {
super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks,
bindInstantServiceAllowed, verbose);
- mCallbacks = callbacks;
}
@Nullable
- public static ComponentName getComponentName(@NonNull Context context,
- @NonNull String componentName, @UserIdInt int userId, boolean isTemporary) {
+ public static ComponentName getComponentName(@NonNull String componentName,
+ @UserIdInt int userId, boolean isTemporary) {
int flags = PackageManager.GET_META_DATA;
if (!isTemporary) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
@@ -88,9 +84,8 @@ final class RemoteAugmentedAutofillService
}
@Override // from AbstractRemoteService
- protected IInterface getServiceInterface(IBinder service) {
- mService = IAugmentedAutofillService.Stub.asInterface(service);
- return mService;
+ protected IAugmentedAutofillService getServiceInterface(IBinder service) {
+ return IAugmentedAutofillService.Stub.asInterface(service);
}
@Override // from AbstractRemoteService
@@ -109,7 +104,6 @@ final class RemoteAugmentedAutofillService
public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client,
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
@Nullable AutofillValue focusedValue) {
- cancelScheduledUnbind();
scheduleRequest(new PendingAutofillRequest(this, sessionId, client, taskId,
activityComponent, focusedId, focusedValue));
}
@@ -118,12 +112,12 @@ final class RemoteAugmentedAutofillService
* Called by {@link Session} when it's time to destroy all augmented autofill requests.
*/
public void onDestroyAutofillWindowsRequest(int sessionId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+ scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId));
}
+ // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
private abstract static class MyPendingRequest
- extends PendingRequest<RemoteAugmentedAutofillService> {
+ extends PendingRequest<RemoteAugmentedAutofillService, IAugmentedAutofillService> {
protected final int mSessionId;
private MyPendingRequest(@NonNull RemoteAugmentedAutofillService service, int sessionId) {
@@ -196,38 +190,8 @@ final class RemoteAugmentedAutofillService
}
- private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
-
- protected PendingDestroyAutofillWindowsRequest(
- @NonNull RemoteAugmentedAutofillService service, @NonNull int sessionId) {
- super(service, sessionId);
- }
-
- @Override
- public void run() {
- final RemoteAugmentedAutofillService remoteService = getService();
- if (remoteService == null) return;
-
- try {
- remoteService.mService.onDestroyFillWindowRequest(mSessionId);
- } catch (RemoteException e) {
- Slog.w(TAG, "exception handling onDestroyAutofillWindowsRequest() for "
- + mSessionId + ": " + e);
- } finally {
- // Service is not calling back, so we finish right away.
- finish();
- }
- }
-
- @Override
- protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
- // Should not happen because we called finish() on run(), although currently it might
- // be called if the service is destroyed while showing it.
- Slog.e(TAG, "timed out: " + this);
- }
- }
-
- public interface RemoteAugmentedAutofillServiceCallbacks extends VultureCallback {
+ public interface RemoteAugmentedAutofillServiceCallbacks
+ extends VultureCallback<RemoteAugmentedAutofillService> {
// NOTE: so far we don't need to notify the callback implementation (an inner class on
// AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
// callback interface is empty.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4b7d29025730..417ea9c84393 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -28,7 +28,6 @@ import android.content.Context;
import android.content.IntentSender;
import android.os.IBinder;
import android.os.ICancellationSignal;
-import android.os.IInterface;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillRequest;
@@ -42,15 +41,15 @@ import android.util.Slog;
import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
-final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
+final class RemoteFillService
+ extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private final FillServiceCallbacks mCallbacks;
- private IAutoFillService mAutoFillService;
- public interface FillServiceCallbacks extends VultureCallback {
+ public interface FillServiceCallbacks extends VultureCallback<RemoteFillService> {
void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@NonNull String servicePackageName, int requestFlags);
void onFillRequestFailure(int requestId, @Nullable CharSequence message);
@@ -71,21 +70,20 @@ final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<
@Override // from AbstractRemoteService
protected void handleOnConnectedStateChanged(boolean state) {
- if (mAutoFillService == null) {
+ if (mService == null) {
Slog.w(mTag, "onConnectedStateChanged(): null service");
return;
}
try {
- mAutoFillService.onConnectedStateChanged(state);
+ mService.onConnectedStateChanged(state);
} catch (Exception e) {
Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
}
}
@Override // from AbstractRemoteService
- protected IInterface getServiceInterface(IBinder service) {
- mAutoFillService = IAutoFillService.Stub.asInterface(service);
- return mAutoFillService;
+ protected IAutoFillService getServiceInterface(IBinder service) {
+ return IAutoFillService.Stub.asInterface(service);
}
@Override // from AbstractRemoteService
@@ -127,17 +125,15 @@ final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<
}
public void onFillRequest(@NonNull FillRequest request) {
- cancelScheduledUnbind();
scheduleRequest(new PendingFillRequest(request, this));
}
public void onSaveRequest(@NonNull SaveRequest request) {
- cancelScheduledUnbind();
scheduleRequest(new PendingSaveRequest(request, this));
}
private boolean handleResponseCallbackCommon(
- @NonNull PendingRequest<RemoteFillService> pendingRequest) {
+ @NonNull PendingRequest<RemoteFillService, IAutoFillService> pendingRequest) {
if (isDestroyed()) return false;
if (mPendingRequest == pendingRequest) {
@@ -204,7 +200,8 @@ final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<
});
}
- private static final class PendingFillRequest extends PendingRequest<RemoteFillService> {
+ private static final class PendingFillRequest
+ extends PendingRequest<RemoteFillService, IAutoFillService> {
private final FillRequest mRequest;
private final IFillCallback mCallback;
private ICancellationSignal mCancellation;
@@ -282,7 +279,7 @@ final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<
if (remoteService != null) {
if (sVerbose) Slog.v(mTag, "calling onFillRequest() for id=" + mRequest.getId());
try {
- remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
+ remoteService.mService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(mTag, "Error calling on fill request", e);
@@ -310,7 +307,8 @@ final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<
}
}
- private static final class PendingSaveRequest extends PendingRequest<RemoteFillService> {
+ private static final class PendingSaveRequest
+ extends PendingRequest<RemoteFillService, IAutoFillService> {
private final SaveRequest mRequest;
private final ISaveCallback mCallback;
@@ -355,7 +353,7 @@ final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<
if (remoteService != null) {
if (sVerbose) Slog.v(mTag, "calling onSaveRequest()");
try {
- remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
+ remoteService.mService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(mTag, "Error calling on save request", e);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d76a5dff36d3..0e9b407372e0 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -98,7 +98,6 @@ import com.android.internal.util.ArrayUtils;
import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
-import com.android.server.infra.AbstractRemoteService;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -909,7 +908,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// VultureCallback
@Override
- public void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
+ public void onServiceDied(@NonNull RemoteFillService service) {
Slog.w(TAG, "removing session because service died");
forceRemoveSelfLocked();
}
@@ -1238,7 +1237,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- final UserData userData = mService.getUserData();
+ final UserData packageUserData = lastResponse.getUserData();
+
+ final UserData userData;
+ if (packageUserData != null) {
+ // Replace default userData
+ userData = packageUserData;
+ } else {
+ userData = mService.getUserData();
+ }
for (int i = 0; i < mViewStates.size(); i++) {
final ViewState viewState = mViewStates.valueAt(i);
@@ -1394,6 +1401,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final String[] userValues = userData.getValues();
final String[] categoryIds = userData.getCategoryIds();
+ final String defaultAlgorithm = userData.getFieldClassificationAlgorithm();
+ final Bundle defaultArgs = userData.getDefaultFieldClassificationArgs();
+
+ final ArrayMap<String, String> algorithms = userData.getFieldClassificationAlgorithms();
+ final ArrayMap<String, Bundle> args = userData.getFieldClassificationArgs();
+
// Sanity check
if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) {
final int valuesLength = userValues == null ? -1 : userValues.length;
@@ -1409,8 +1422,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>(
maxFieldsSize);
- final String algorithm = userData.getFieldClassificationAlgorithm();
- final Bundle algorithmArgs = userData.getAlgorithmArgs();
final int viewsSize = viewStates.size();
// First, we get all scores.
@@ -1497,7 +1508,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mComponentName, mCompatMode);
});
- fcStrategy.getScores(callback, algorithm, algorithmArgs, currentValues, userValues);
+ fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
+ defaultAlgorithm, defaultArgs, algorithms, args);
}
/**
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3acdc8e3aeb9..a917ced2ae68 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,7 +16,9 @@
package com.android.server.backup;
+import android.Manifest;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -30,6 +32,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Environment;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -79,6 +82,7 @@ public class BackupManagerService {
return sInstance;
}
+ private final Context mContext;
private UserBackupManagerService mUserBackupManagerService;
/** Instantiate a new instance of {@link BackupManagerService}. */
@@ -91,11 +95,26 @@ public class BackupManagerService {
transportWhitelist = Collections.emptySet();
}
+ mContext = context;
mUserBackupManagerService =
UserBackupManagerService.createAndInitializeService(
context, trampoline, backupThread, transportWhitelist);
}
+ /**
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id on which the backup operation is being requested.
+ * @param message A message to include in the exception if it is thrown.
+ */
+ private void enforceCallingPermissionOnUserId(int userId, String message) {
+ if (Binder.getCallingUserHandle().getIdentifier() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ }
+ }
+
// TODO(b/118520567): Remove when tests are modified to use per-user instance.
@VisibleForTesting
void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
@@ -314,7 +333,8 @@ public class BackupManagerService {
// ---------------------------------------------
/** Enable/disable the backup service. This is user-configurable via backup settings. */
- public void setBackupEnabled(boolean enable) {
+ public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
+ enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
mUserBackupManagerService.setBackupEnabled(enable);
}
@@ -331,7 +351,8 @@ public class BackupManagerService {
/**
* Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
- public boolean isBackupEnabled() {
+ public boolean isBackupEnabled(@UserIdInt int userId) {
+ enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
return mUserBackupManagerService.isBackupEnabled();
}
@@ -355,7 +376,8 @@ public class BackupManagerService {
* Run a backup pass immediately for any key-value backup applications that have declared that
* they have pending updates.
*/
- public void backupNow() {
+ public void backupNow(@UserIdInt int userId) {
+ enforceCallingPermissionOnUserId(userId, "backupNow");
mUserBackupManagerService.backupNow();
}
@@ -364,12 +386,18 @@ public class BackupManagerService {
* IBackupManagerMonitor} for receiving events during the operation.
*/
public int requestBackup(
- String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+ @UserIdInt int userId,
+ String[] packages,
+ IBackupObserver observer,
+ IBackupManagerMonitor monitor,
+ int flags) {
+ enforceCallingPermissionOnUserId(userId, "requestBackup");
return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
}
/** Cancel all running backup operations. */
- public void cancelBackups() {
+ public void cancelBackups(@UserIdInt int userId) {
+ enforceCallingPermissionOnUserId(userId, "cancelBackups");
mUserBackupManagerService.cancelBackups();
}
@@ -533,8 +561,10 @@ public class BackupManagerService {
stage.renameTo(enableFile);
// will be synced immediately by the try-with-resources call to close()
} catch (IOException | RuntimeException e) {
- Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
- + e.getMessage());
+ Slog.e(
+ TAG,
+ "Unable to record backup enable state; reverting to disabled: "
+ + e.getMessage());
enableFile.delete();
stage.delete();
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 108f50d922f8..ed6ff9b1d77d 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -19,6 +19,7 @@ package com.android.server.backup;
import static com.android.server.backup.BackupManagerService.TAG;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManager;
@@ -123,6 +124,10 @@ public class Trampoline extends IBackupManager.Stub {
== MULTI_USER_ENABLED;
}
+ protected int binderGetCallingUserId() {
+ return Binder.getCallingUserHandle().getIdentifier();
+ }
+
protected int binderGetCallingUid() {
return Binder.getCallingUid();
}
@@ -319,14 +324,20 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+ public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.setBackupEnabled(isEnabled);
+ svc.setBackupEnabled(userId, isEnabled);
}
}
@Override
+ public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+ setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
+ }
+
+ @Override
public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
@@ -343,9 +354,14 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public boolean isBackupEnabled() throws RemoteException {
+ public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.isBackupEnabled() : false;
+ return (svc != null) ? svc.isBackupEnabled(userId) : false;
+ }
+
+ @Override
+ public boolean isBackupEnabled() throws RemoteException {
+ return isBackupEnabledForUser(binderGetCallingUserId());
}
@Override
@@ -361,14 +377,19 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public void backupNow() throws RemoteException {
+ public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.backupNow();
+ svc.backupNow(userId);
}
}
@Override
+ public void backupNow() throws RemoteException {
+ backupNowForUser(binderGetCallingUserId());
+ }
+
+ @Override
public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
boolean includeShared, boolean doWidgets, boolean allApps,
boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames)
@@ -543,24 +564,36 @@ public class Trampoline extends IBackupManager.Stub {
}
@Override
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) throws RemoteException {
+ public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
+ observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
BackupManagerService svc = mService;
if (svc == null) {
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
- return svc.requestBackup(packages, observer, monitor, flags);
+ return svc.requestBackup(userId, packages, observer, monitor, flags);
}
@Override
- public void cancelBackups() throws RemoteException {
+ public int requestBackup(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags) throws RemoteException {
+ return requestBackupForUser(binderGetCallingUserId(), packages,
+ observer, monitor, flags);
+ }
+
+ @Override
+ public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.cancelBackups();
+ svc.cancelBackups(userId);
}
}
@Override
+ public void cancelBackups() throws RemoteException {
+ cancelBackupsForUser(binderGetCallingUserId());
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
index 2302b7db3723..a4012d5faa8a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureSession.java
@@ -29,7 +29,6 @@ import android.view.contentcapture.ContentCaptureEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
-import com.android.server.infra.AbstractRemoteService;
import java.io.PrintWriter;
import java.util.List;
@@ -124,8 +123,8 @@ final class ContentCaptureSession implements ContentCaptureServiceCallbacks {
}
}
- @Override // from RemoteScreenObservationServiceCallbacks
- public void onServiceDied(AbstractRemoteService<?> service) {
+ @Override // from RemoteContentCaptureServiceCallbacks
+ public void onServiceDied(@NonNull RemoteContentCaptureService service) {
// TODO(b/111276913): implement (remove session from PerUserSession?)
if (mService.isDebug()) {
Slog.d(TAG, "onServiceDied() for " + mId);
@@ -135,17 +134,6 @@ final class ContentCaptureSession implements ContentCaptureServiceCallbacks {
}
}
- @Override // from RemoteScreenObservationServiceCallbacks
- public void onFailureOrTimeout(boolean timedOut) {
- // TODO(b/111276913): log metrics on whether timed out or not
- if (mService.isDebug()) {
- Slog.d(TAG, "onFailureOrTimeout(" + mId + "): timed out=" + timedOut);
- }
- synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ false);
- }
- }
-
@GuardedBy("mLock")
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 6a111f238073..33b6c8d5eec4 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -20,14 +20,11 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
-import android.os.IInterface;
-import android.os.RemoteException;
import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.InteractionContext;
import android.service.contentcapture.SnapshotData;
import android.text.format.DateUtils;
-import android.util.Slog;
import android.view.contentcapture.ContentCaptureEvent;
import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
@@ -35,30 +32,24 @@ import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
import java.util.List;
final class RemoteContentCaptureService
- extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService> {
-
- private static final String TAG = RemoteContentCaptureService.class.getSimpleName();
+ extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
+ IContentCaptureService> {
// TODO(b/117779333): changed it so it's permanentely bound
private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
- private final ContentCaptureServiceCallbacks mCallbacks;
- private IContentCaptureService mService;
-
RemoteContentCaptureService(Context context, String serviceInterface,
ComponentName componentName, int userId,
ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callbacks,
bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
- mCallbacks = callbacks;
}
@Override // from RemoteService
- protected IInterface getServiceInterface(@NonNull IBinder service) {
- mService = IContentCaptureService.Stub.asInterface(service);
- return mService;
+ protected IContentCaptureService getServiceInterface(@NonNull IBinder service) {
+ return IContentCaptureService.Stub.asInterface(service);
}
// TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
@@ -81,8 +72,7 @@ final class RemoteContentCaptureService
*/
public void onSessionLifecycleRequest(@Nullable InteractionContext context,
@NonNull String sessionId) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
+ scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
}
/**
@@ -90,8 +80,8 @@ final class RemoteContentCaptureService
*/
public void onContentCaptureEventsRequest(@NonNull String sessionId,
@NonNull List<ContentCaptureEvent> events) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
+ scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
+ new ContentCaptureEventsRequest(events)));
}
/**
@@ -99,103 +89,13 @@ final class RemoteContentCaptureService
*/
public void onActivitySnapshotRequest(@NonNull String sessionId,
@NonNull SnapshotData snapshotData) {
- cancelScheduledUnbind();
- scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
- }
-
- private abstract static class MyPendingRequest
- extends PendingRequest<RemoteContentCaptureService> {
- protected final String mSessionId;
-
- private MyPendingRequest(@NonNull RemoteContentCaptureService service,
- @NonNull String sessionId) {
- super(service);
- mSessionId = sessionId;
- }
-
- @Override // from PendingRequest
- protected final void onTimeout(RemoteContentCaptureService remoteService) {
- Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
- + mSessionId);
- remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
- }
-
- @Override // from PendingRequest
- public final void run() {
- final RemoteContentCaptureService remoteService = getService();
- if (remoteService != null) {
- try {
- // We don't expect the service to call us back, so we finish right away.
- myRun(remoteService);
- // TODO(b/111330312): not true anymore!!
- finish();
- } catch (RemoteException e) {
- Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
- + mSessionId + ": " + e);
- remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ false);
- }
- }
- }
-
- protected abstract void myRun(@NonNull RemoteContentCaptureService service)
- throws RemoteException;
-
- }
-
- private static final class PendingSessionLifecycleRequest extends MyPendingRequest {
-
- private final InteractionContext mContext;
-
- protected PendingSessionLifecycleRequest(@NonNull RemoteContentCaptureService service,
- @Nullable InteractionContext context, @NonNull String sessionId) {
- super(service, sessionId);
- mContext = context;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteContentCaptureService remoteService)
- throws RemoteException {
- remoteService.mService.onSessionLifecycle(mContext, mSessionId);
- }
- }
-
- private static final class PendingOnContentCaptureEventsRequest extends MyPendingRequest {
-
- private final List<ContentCaptureEvent> mEvents;
-
- protected PendingOnContentCaptureEventsRequest(@NonNull RemoteContentCaptureService service,
- @NonNull String sessionId, @NonNull List<ContentCaptureEvent> events) {
- super(service, sessionId);
- mEvents = events;
- }
-
- @Override // from MyPendingRequest
- public void myRun(@NonNull RemoteContentCaptureService remoteService)
- throws RemoteException {
- remoteService.mService.onContentCaptureEventsRequest(mSessionId,
- new ContentCaptureEventsRequest(mEvents));
- }
- }
-
- private static final class PendingOnActivitySnapshotRequest extends MyPendingRequest {
-
- private final SnapshotData mSnapshotData;
-
- protected PendingOnActivitySnapshotRequest(@NonNull RemoteContentCaptureService service,
- @NonNull String sessionId, @NonNull SnapshotData snapshotData) {
- super(service, sessionId);
- mSnapshotData = snapshotData;
- }
-
- @Override // from MyPendingRequest
- protected void myRun(@NonNull RemoteContentCaptureService remoteService)
- throws RemoteException {
- remoteService.mService.onActivitySnapshot(mSessionId, mSnapshotData);
- }
+ scheduleAsyncRequest((s) -> s.onActivitySnapshot(sessionId, snapshotData));
}
- public interface ContentCaptureServiceCallbacks extends VultureCallback {
- // To keep it simple, we use the same callback for all failures / timeouts.
- void onFailureOrTimeout(boolean timedOut);
+ public interface ContentCaptureServiceCallbacks
+ extends VultureCallback<RemoteContentCaptureService> {
+ // NOTE: so far we don't need to notify the callback implementation (an inner class on
+ // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
+ // callback interface is empty.
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index cccacf4bf837..4408db895537 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -7,7 +7,6 @@ java_library_static {
"frameworks/native/aidl/binder",
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
- "system/netd/server/binder",
"system/vold/binder",
],
},
@@ -21,7 +20,6 @@ java_library_static {
":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
- ":netd_metrics_aidl",
],
libs: [
@@ -52,6 +50,7 @@ java_library_static {
"android.hardware.contexthub-V1.0-java",
"android.hidl.manager-V1.0-java",
"netd_aidl_interface-java",
+ "netd_event_listener_interface-java",
],
}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 11a2fc9c1e45..8b7f321eb087 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -19,7 +19,6 @@ package com.android.server;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -28,9 +27,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Process;
-import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.ThreadLocalWorkSource;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -38,19 +35,16 @@ import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AppIdToPackageMap;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
-import com.android.internal.os.BinderInternal.CallSession;
import com.android.internal.os.CachedDeviceState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class BinderCallsStatsService extends Binder {
@@ -60,10 +54,10 @@ public class BinderCallsStatsService extends Binder {
= "persist.sys.binder_calls_detailed_tracking";
/** Resolves the work source of an incoming binder transaction. */
- static class WorkSourceProvider {
+ static class AuthorizedWorkSourceProvider implements BinderInternal.WorkSourceProvider {
private ArraySet<Integer> mAppIdWhitelist;
- WorkSourceProvider() {
+ AuthorizedWorkSourceProvider() {
mAppIdWhitelist = new ArraySet<>();
}
@@ -82,11 +76,11 @@ public class BinderCallsStatsService extends Binder {
mAppIdWhitelist = createAppidWhitelist(context);
}
- public void dump(PrintWriter pw, Map<Integer, String> appIdToPackageName) {
+ public void dump(PrintWriter pw, AppIdToPackageMap packageMap) {
pw.println("AppIds of apps that can set the work source:");
final ArraySet<Integer> whitelist = mAppIdWhitelist;
for (Integer appId : whitelist) {
- pw.println("\t- " + appIdToPackageName.getOrDefault(appId, String.valueOf(appId)));
+ pw.println("\t- " + packageMap.mapAppId(appId));
}
}
@@ -103,7 +97,7 @@ public class BinderCallsStatsService extends Binder {
final ArraySet<Integer> whitelist = new ArraySet<>();
// We trust our own process.
- whitelist.add(Process.myUid());
+ whitelist.add(UserHandle.getAppId(Process.myUid()));
// We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission.
final PackageManager pm = context.getPackageManager();
final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS };
@@ -125,41 +119,6 @@ public class BinderCallsStatsService extends Binder {
}
}
- /** Observer for all system server incoming binder transactions. */
- @VisibleForTesting
- static class BinderCallsObserver implements BinderInternal.Observer {
- private final BinderInternal.Observer mBinderCallsStats;
- private final WorkSourceProvider mWorkSourceProvider;
-
- BinderCallsObserver(BinderInternal.Observer callsStats,
- WorkSourceProvider workSourceProvider) {
- mBinderCallsStats = callsStats;
- mWorkSourceProvider = workSourceProvider;
- }
-
- @Override
- public CallSession callStarted(Binder binder, int code) {
- // We depend on the code in Binder#execTransact to reset the state of
- // ThreadLocalWorkSource
- setThreadLocalWorkSourceUid(mWorkSourceProvider.resolveWorkSourceUid());
- return mBinderCallsStats.callStarted(binder, code);
- }
-
- @Override
- public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
- mBinderCallsStats.callEnded(s, parcelRequestSize, parcelReplySize);
- }
-
- @Override
- public void callThrewException(CallSession s, Exception exception) {
- mBinderCallsStats.callThrewException(s, exception);
- }
-
- protected void setThreadLocalWorkSourceUid(int uid) {
- ThreadLocalWorkSource.setUid(uid);
- }
- }
-
/** Listens for flag changes. */
private static class SettingsObserver extends ContentObserver {
private static final String SETTINGS_ENABLED_KEY = "enabled";
@@ -173,16 +132,16 @@ public class BinderCallsStatsService extends Binder {
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private final BinderCallsStats mBinderCallsStats;
- private final BinderCallsObserver mBinderCallsObserver;
+ private final AuthorizedWorkSourceProvider mWorkSourceProvider;
SettingsObserver(Context context, BinderCallsStats binderCallsStats,
- BinderCallsObserver observer) {
+ AuthorizedWorkSourceProvider workSourceProvider) {
super(BackgroundThread.getHandler());
mContext = context;
context.getContentResolver().registerContentObserver(mUri, false, this,
UserHandle.USER_SYSTEM);
mBinderCallsStats = binderCallsStats;
- mBinderCallsObserver = observer;
+ mWorkSourceProvider = workSourceProvider;
// Always kick once to ensure that we match current state
onChange();
}
@@ -220,12 +179,14 @@ public class BinderCallsStatsService extends Binder {
mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
if (mEnabled != enabled) {
if (enabled) {
- Binder.setObserver(mBinderCallsObserver);
+ Binder.setObserver(mBinderCallsStats);
Binder.setProxyTransactListener(
new Binder.PropagateWorkSourceTransactListener());
+ Binder.setWorkSourceProvider(mWorkSourceProvider);
} else {
Binder.setObserver(null);
Binder.setProxyTransactListener(null);
+ Binder.setWorkSourceProvider(Binder::getCallingUid);
}
mEnabled = enabled;
mBinderCallsStats.reset();
@@ -268,7 +229,7 @@ public class BinderCallsStatsService extends Binder {
public static class LifeCycle extends SystemService {
private BinderCallsStatsService mService;
private BinderCallsStats mBinderCallsStats;
- private WorkSourceProvider mWorkSourceProvider;
+ private AuthorizedWorkSourceProvider mWorkSourceProvider;
public LifeCycle(Context context) {
super(context);
@@ -277,11 +238,9 @@ public class BinderCallsStatsService extends Binder {
@Override
public void onStart() {
mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
- mWorkSourceProvider = new WorkSourceProvider();
- BinderCallsObserver binderCallsObserver =
- new BinderCallsObserver(mBinderCallsStats, mWorkSourceProvider);
+ mWorkSourceProvider = new AuthorizedWorkSourceProvider();
mService = new BinderCallsStatsService(
- mBinderCallsStats, binderCallsObserver, mWorkSourceProvider);
+ mBinderCallsStats, mWorkSourceProvider);
publishLocalService(Internal.class, new Internal(mBinderCallsStats));
publishBinderService("binder_calls_stats", mService);
boolean detailedTrackingEnabled = SystemProperties.getBoolean(
@@ -311,18 +270,16 @@ public class BinderCallsStatsService extends Binder {
private SettingsObserver mSettingsObserver;
private final BinderCallsStats mBinderCallsStats;
- private final BinderCallsObserver mBinderCallsObserver;
- private final WorkSourceProvider mWorkSourceProvider;
+ private final AuthorizedWorkSourceProvider mWorkSourceProvider;
- BinderCallsStatsService(BinderCallsStats binderCallsStats, BinderCallsObserver observer,
- WorkSourceProvider workSourceProvider) {
+ BinderCallsStatsService(BinderCallsStats binderCallsStats,
+ AuthorizedWorkSourceProvider workSourceProvider) {
mBinderCallsStats = binderCallsStats;
- mBinderCallsObserver = observer;
mWorkSourceProvider = workSourceProvider;
}
public void systemReady(Context context) {
- mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mBinderCallsObserver);
+ mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mWorkSourceProvider);
}
public void reset() {
@@ -342,7 +299,7 @@ public class BinderCallsStatsService extends Binder {
pw.println("binder_calls_stats reset.");
return;
} else if ("--enable".equals(arg)) {
- Binder.setObserver(mBinderCallsObserver);
+ Binder.setObserver(mBinderCallsStats);
return;
} else if ("--disable".equals(arg)) {
Binder.setObserver(null);
@@ -361,7 +318,7 @@ public class BinderCallsStatsService extends Binder {
pw.println("Detailed tracking disabled");
return;
} else if ("--dump-worksource-provider".equals(arg)) {
- mWorkSourceProvider.dump(pw, getAppIdToPackagesMap());
+ mWorkSourceProvider.dump(pw, AppIdToPackageMap.getSnapshot());
return;
} else if ("-h".equals(arg)) {
pw.println("binder_calls_stats commands:");
@@ -377,28 +334,6 @@ public class BinderCallsStatsService extends Binder {
}
}
}
- mBinderCallsStats.dump(pw, getAppIdToPackagesMap(), verbose);
- }
-
- private Map<Integer, String> getAppIdToPackagesMap() {
- List<PackageInfo> packages;
- try {
- packages = AppGlobals.getPackageManager()
- .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES,
- UserHandle.USER_SYSTEM).getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- Map<Integer,String> map = new HashMap<>();
- for (PackageInfo pkg : packages) {
- String name = pkg.packageName;
- int uid = pkg.applicationInfo.uid;
- // Use sharedUserId string as package name if there are collisions
- if (pkg.sharedUserId != null && map.containsKey(uid)) {
- name = "shared:" + pkg.sharedUserId;
- }
- map.put(uid, name);
- }
- return map;
+ mBinderCallsStats.dump(pw, AppIdToPackageMap.getSnapshot(), verbose);
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 14503f9d7379..eda9fe15fe36 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -902,6 +902,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Listen to package add and removal events for all users.
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiverAsUser(
@@ -4203,12 +4204,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPermissionMonitor.onPackageAdded(packageName, uid);
}
- private void onPackageRemoved(String packageName, int uid) {
+ private void onPackageReplaced(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+ return;
+ }
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ return;
+ }
+ // Legacy always-on VPN won't be affected since the package name is not set.
+ if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
+ Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ + userId);
+ vpn.startAlwaysOnVpn();
+ }
+ }
+ }
+
+ private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
return;
}
mPermissionMonitor.onPackageRemoved(uid);
+
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ return;
+ }
+ // Legacy always-on VPN won't be affected since the package name is not set.
+ if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
+ Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ + userId);
+ vpn.setAlwaysOnPackage(null, false);
+ }
+ }
}
private void onUserUnlocked(int userId) {
@@ -4245,8 +4280,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
onUserUnlocked(userId);
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
onPackageAdded(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ onPackageReplaced(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- onPackageRemoved(packageName, uid);
+ final boolean isReplacing = intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, false);
+ onPackageRemoved(packageName, uid, isReplacing);
}
}
};
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cc7bf3373bdd..8b992ebcf059 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,8 +17,11 @@
package com.android.server;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationProvider.AVAILABLE;
import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
+import static com.android.internal.util.Preconditions.checkState;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -47,14 +50,12 @@ import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
import android.location.IGpsGeofenceHardware;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
-import android.location.LocationProvider;
import android.location.LocationRequest;
import android.os.Binder;
import android.os.Bundle;
@@ -84,6 +85,7 @@ import com.android.internal.location.ProviderRequest;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
@@ -92,9 +94,9 @@ import com.android.server.location.GnssBatchingProvider;
import com.android.server.location.GnssLocationProvider;
import com.android.server.location.GnssMeasurementsProvider;
import com.android.server.location.GnssNavigationMessageProvider;
+import com.android.server.location.GnssStatusListenerHelper;
import com.android.server.location.LocationBlacklist;
import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
import com.android.server.location.LocationRequestStatistics;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
@@ -131,8 +133,6 @@ public class LocationManagerService extends ILocationManager.Stub {
// Location resolution level: fine location data
private static final int RESOLUTION_LEVEL_FINE = 2;
- private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
private static final String INSTALL_LOCATION_PROVIDER =
@@ -182,7 +182,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private ActivityManager mActivityManager;
private UserManager mUserManager;
private GeocoderProxy mGeocodeProvider;
- private IGnssStatusProvider mGnssStatusProvider;
+ private GnssStatusListenerHelper mGnssStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
@@ -192,12 +192,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private IGpsGeofenceHardware mGpsGeofenceProxy;
// --- fields below are protected by mLock ---
- // Set of providers that are explicitly enabled
- // Only used by passive, fused & test. Network & GPS are controlled separately, and not listed.
- private final Set<String> mEnabledProviders = new HashSet<>();
-
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<>();
// Mock (test) providers
private final HashMap<String, MockProvider> mMockProviders =
@@ -207,15 +201,15 @@ public class LocationManagerService extends ILocationManager.Stub {
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
// currently installed providers (with mocks replacing real providers)
- private final ArrayList<LocationProviderInterface> mProviders =
+ private final ArrayList<LocationProvider> mProviders =
new ArrayList<>();
// real providers, saved here when mocked out
- private final HashMap<String, LocationProviderInterface> mRealProviders =
+ private final HashMap<String, LocationProvider> mRealProviders =
new HashMap<>();
// mapping from provider name to provider
- private final HashMap<String, LocationProviderInterface> mProvidersByName =
+ private final HashMap<String, LocationProvider> mProvidersByName =
new HashMap<>();
// mapping from provider name to all its UpdateRecords
@@ -270,13 +264,8 @@ public class LocationManagerService extends ILocationManager.Stub {
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
packageManagerInternal.setLocationPackagesProvider(
- new PackageManagerInternal.PackagesProvider() {
- @Override
- public String[] getPackages(int userId) {
- return mContext.getResources().getStringArray(
- com.android.internal.R.array.config_locationProviderPackageNames);
- }
- });
+ userId -> mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationProviderPackageNames));
if (D) Log.d(TAG, "Constructed");
@@ -321,30 +310,17 @@ public class LocationManagerService extends ILocationManager.Stub {
mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
- PackageManager.OnPermissionsChangedListener permissionListener
- = new PackageManager.OnPermissionsChangedListener() {
- @Override
- public void onPermissionsChanged(final int uid) {
- synchronized (mLock) {
- applyAllProviderRequirementsLocked();
- }
+ PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+ synchronized (mLock) {
+ applyAllProviderRequirementsLocked();
}
};
mPackageManager.addOnPermissionsChangeListener(permissionListener);
// listen for background/foreground changes
- ActivityManager.OnUidImportanceListener uidImportanceListener
- = new ActivityManager.OnUidImportanceListener() {
- @Override
- public void onUidImportance(final int uid, final int importance) {
- mLocationHandler.post(new Runnable() {
- @Override
- public void run() {
- onUidImportanceChanged(uid, importance);
- }
- });
- }
- };
+ ActivityManager.OnUidImportanceListener uidImportanceListener =
+ (uid, importance) -> mLocationHandler.post(
+ () -> onUidImportanceChanged(uid, importance));
mActivityManager.addOnUidImportanceListener(uidImportanceListener,
FOREGROUND_IMPORTANCE_CUTOFF);
@@ -356,7 +332,10 @@ public class LocationManagerService extends ILocationManager.Stub {
// prepare providers
loadProvidersLocked();
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
// listen for settings changes
@@ -366,7 +345,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
}
}
}, UserHandle.USER_ALL);
@@ -377,7 +356,9 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateProvidersLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
}
}, UserHandle.USER_ALL);
@@ -402,7 +383,9 @@ public class LocationManagerService extends ILocationManager.Stub {
public void onChange(boolean selfChange) {
synchronized (mLock) {
updateBackgroundThrottlingWhitelistLocked();
- updateProvidersLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
}
}, UserHandle.USER_ALL);
@@ -414,7 +397,6 @@ public class LocationManagerService extends ILocationManager.Stub {
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- intentFilter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
@@ -425,12 +407,6 @@ public class LocationManagerService extends ILocationManager.Stub {
} else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
updateUserProfiles(mCurrentUserId);
- } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
- // shutdown only if UserId indicates whole system, not just one user
- if (D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
- if (getSendingUserId() == UserHandle.USER_ALL) {
- shutdownComponents();
- }
}
}
}, UserHandle.ALL, intentFilter, null, mLocationHandler);
@@ -463,14 +439,16 @@ public class LocationManagerService extends ILocationManager.Stub {
}
for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
- if (entry.getValue().mUid == uid) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
if (D) {
Log.d(TAG, "gnss measurements listener from uid " + uid
+ " is now " + (foreground ? "foreground" : "background)"));
}
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssMeasurementsProvider.addListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+ IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
} else {
mGnssMeasurementsProvider.removeListener(
IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
@@ -479,7 +457,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
- if (entry.getValue().mUid == uid) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
if (D) {
Log.d(TAG, "gnss navigation message listener from uid "
+ uid + " is now "
@@ -487,13 +466,16 @@ public class LocationManagerService extends ILocationManager.Stub {
}
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssNavigationMessageProvider.addListener(
- IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+ IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
} else {
mGnssNavigationMessageProvider.removeListener(
IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
}
}
}
+
+ // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
}
}
@@ -502,30 +484,13 @@ public class LocationManagerService extends ILocationManager.Stub {
}
/**
- * Provides a way for components held by the {@link LocationManagerService} to clean-up
- * gracefully on system's shutdown.
- *
- * NOTES:
- * 1) Only provides a chance to clean-up on an opt-in basis. This guarantees back-compat
- * support for components that do not wish to handle such event.
- */
- private void shutdownComponents() {
- if (D) Log.d(TAG, "Shutting down components...");
-
- LocationProviderInterface gpsProvider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
- if (gpsProvider != null && gpsProvider.isEnabled()) {
- gpsProvider.disable();
- }
- }
-
- /**
* Makes a list of userids that are related to the current user. This is
* relevant when using managed profiles. Otherwise the list only contains
* the current user.
*
* @param currentUserId the current user, who might have an alter-ego.
*/
- void updateUserProfiles(int currentUserId) {
+ private void updateUserProfiles(int currentUserId) {
int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
synchronized (mLock) {
mCurrentUserProfiles = profileIds;
@@ -614,22 +579,28 @@ public class LocationManagerService extends ILocationManager.Stub {
private void loadProvidersLocked() {
// create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProviderLocked(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
+ LocationProvider passiveProviderManager = new LocationProvider(
+ LocationManager.PASSIVE_PROVIDER);
+ PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
+
+ addProviderLocked(passiveProviderManager);
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
// Create a gps location provider
- GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
+ LocationProvider gnssProviderManager = new LocationProvider(
+ LocationManager.GPS_PROVIDER);
+ GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
+ gnssProviderManager,
mLocationHandler.getLooper());
+
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- addProviderLocked(gnssProvider);
- mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
+ addProviderLocked(gnssProviderManager);
+ mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
@@ -657,34 +628,38 @@ public class LocationManagerService extends ILocationManager.Stub {
ensureFallbackFusedProviderPresentLocked(pkgs);
// bind to network provider
+
+ LocationProvider networkProviderManager = new LocationProvider(
+ LocationManager.NETWORK_PROVIDER);
LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
mContext,
- LocationManager.NETWORK_PROVIDER,
+ networkProviderManager,
NETWORK_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
com.android.internal.R.string.config_networkLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (networkProvider != null) {
- mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+ mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
mProxyProviders.add(networkProvider);
- addProviderLocked(networkProvider);
+ addProviderLocked(networkProviderManager);
} else {
Slog.w(TAG, "no network location provider found");
}
// bind to fused provider
- LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+ LocationProvider fusedProviderManager = new LocationProvider(
+ LocationManager.FUSED_PROVIDER);
+ LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
mContext,
- LocationManager.FUSED_PROVIDER,
+ fusedProviderManager,
FUSED_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
- if (fusedLocationProvider != null) {
- addProviderLocked(fusedLocationProvider);
- mProxyProviders.add(fusedLocationProvider);
- mEnabledProviders.add(fusedLocationProvider.getName());
- mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
+ if (fusedProvider != null) {
+ addProviderLocked(fusedProviderManager);
+ mProxyProviders.add(fusedProvider);
+ mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
} else {
Slog.e(TAG, "no fused location provider found",
new IllegalStateException("Location service needs a fused location provider"));
@@ -765,12 +740,9 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
mLastLocation.clear();
mLastLocationCoarseInterval.clear();
- for (LocationProviderInterface p : mProviders) {
- updateProviderListenersLocked(p.getName(), false);
- }
- mCurrentUserId = userId;
updateUserProfiles(userId);
- updateProvidersLocked();
+ updateProvidersSettingsLocked();
+ mCurrentUserId = userId;
}
}
@@ -786,6 +758,165 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
+ private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+
+ private final String mName;
+ private AbstractLocationProvider mProvider;
+
+ // whether the provider is enabled in location settings
+ private boolean mSettingsEnabled;
+
+ // whether the provider considers itself enabled
+ private volatile boolean mEnabled;
+
+ @Nullable
+ private volatile ProviderProperties mProperties;
+
+ private LocationProvider(String name) {
+ mName = name;
+ // TODO: initialize settings enabled?
+ }
+
+ @Override
+ public void onAttachProvider(AbstractLocationProvider provider, boolean initiallyEnabled) {
+ checkState(mProvider == null);
+
+ // the provider is not yet fully constructed at this point, so we may not do anything
+ // except save a reference for later use here. do not call any provider methods.
+ mProvider = provider;
+ mEnabled = initiallyEnabled;
+ mProperties = null;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean isEnabled() {
+ return mSettingsEnabled && mEnabled;
+ }
+
+ @Nullable
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ public void setRequest(ProviderRequest request, WorkSource workSource) {
+ mProvider.setRequest(request, workSource);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(mName + " provider:");
+ pw.println(" setting=" + mSettingsEnabled);
+ pw.println(" enabled=" + mEnabled);
+ pw.println(" properties=" + mProperties);
+ mProvider.dump(fd, pw, args);
+ }
+
+ public long getStatusUpdateTime() {
+ return mProvider.getStatusUpdateTime();
+ }
+
+ public int getStatus(Bundle extras) {
+ return mProvider.getStatus(extras);
+ }
+
+ public void sendExtraCommand(String command, Bundle extras) {
+ mProvider.sendExtraCommand(command, extras);
+ }
+
+ // called from any thread
+ @Override
+ public void onReportLocation(Location location) {
+ runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+ mProvider == mPassiveProvider));
+ }
+
+ // called from any thread
+ @Override
+ public void onReportLocation(List<Location> locations) {
+ runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+ }
+
+ // called from any thread
+ @Override
+ public void onSetEnabled(boolean enabled) {
+ runOnHandler(() -> {
+ if (enabled == mEnabled) {
+ return;
+ }
+
+ mEnabled = enabled;
+
+ if (!mSettingsEnabled) {
+ // this provider was disabled in settings anyways, so a change to it's own
+ // enabled status won't have any affect.
+ return;
+ }
+
+ // traditionally clients can listen for changes to the LOCATION_PROVIDERS_ALLOWED
+ // setting to detect when providers are enabled or disabled (even though they aren't
+ // supposed to). to continue to support this we must force a change to this setting.
+ // we use the fused provider because this is forced to be always enabled in settings
+ // anyways, and so won't have any visible effect beyond triggering content observers
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "+" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+
+ synchronized (mLock) {
+ 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.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ }
+
+ updateProviderListenersLocked(mName);
+ }
+
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+ UserHandle.ALL);
+ });
+ }
+
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+ runOnHandler(() -> mProperties = properties);
+ }
+
+ private void setSettingsEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (mSettingsEnabled == enabled) {
+ return;
+ }
+
+ mSettingsEnabled = enabled;
+ if (!mSettingsEnabled) {
+ // 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.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateProviderListenersLocked(mName);
+ } else if (mEnabled) {
+ updateProviderListenersLocked(mName);
+ }
+ }
+ }
+
+ private void runOnHandler(Runnable runnable) {
+ if (Looper.myLooper() == mLocationHandler.getLooper()) {
+ runnable.run();
+ } else {
+ mLocationHandler.post(runnable);
+ }
+ }
+ }
+
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
@@ -793,24 +924,24 @@ public class LocationManagerService extends ILocationManager.Stub {
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
final Identity mIdentity;
- final int mAllowedResolutionLevel; // resolution level allowed to receiver
+ private final int mAllowedResolutionLevel; // resolution level allowed to receiver
- final ILocationListener mListener;
+ private final ILocationListener mListener;
final PendingIntent mPendingIntent;
final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
- final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
- final Object mKey;
+ private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
+ private final Object mKey;
final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
// True if app ops has started monitoring this receiver for locations.
- boolean mOpMonitoring;
+ private boolean mOpMonitoring;
// True if app ops has started monitoring this receiver for high power (gps) locations.
- boolean mOpHighPowerMonitoring;
- int mPendingBroadcasts;
+ private boolean mOpHighPowerMonitoring;
+ private int mPendingBroadcasts;
PowerManager.WakeLock mWakeLock;
- Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+ private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
String packageName, WorkSource workSource, boolean hideFromAppOps) {
mListener = listener;
mPendingIntent = intent;
@@ -885,9 +1016,10 @@ public class LocationManagerService extends ILocationManager.Stub {
// See if receiver has any enabled update records. Also note if any update records
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
+ if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+ mCurrentUserId)) {
requestingLocation = true;
- LocationProviderInterface locationProvider
+ LocationManagerService.LocationProvider locationProvider
= mProvidersByName.get(updateRecord.mProvider);
ProviderProperties properties = locationProvider != null
? locationProvider.getProperties() : null;
@@ -1034,7 +1166,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return true;
}
- public boolean callProviderEnabledLocked(String provider, boolean enabled) {
+ private boolean callProviderEnabledLocked(String provider, boolean enabled) {
// First update AppOp monitoring.
// An app may get/lose location access as providers are enabled/disabled.
updateMonitoring(true);
@@ -1236,7 +1368,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private class LinkedCallback implements IBinder.DeathRecipient {
private final IBatchedLocationCallback mCallback;
- public LinkedCallback(@NonNull IBatchedLocationCallback callback) {
+ private LinkedCallback(@NonNull IBatchedLocationCallback callback) {
mCallback = callback;
}
@@ -1337,7 +1469,7 @@ public class LocationManagerService extends ILocationManager.Stub {
checkCallerIsProvider();
// Currently used only for GNSS locations - update permissions check if changed
- if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) {
+ if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
if (mGnssBatchingCallback == null) {
Slog.e(TAG, "reportLocationBatch() called without active Callback");
return;
@@ -1352,29 +1484,17 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- private void addProviderLocked(LocationProviderInterface provider) {
+ private void addProviderLocked(LocationProvider provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProviderLocked(LocationProviderInterface provider) {
- provider.disable();
+ private void removeProviderLocked(LocationProvider provider) {
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
/**
- * Returns "true" if access to the specified location provider is allowed by the current
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- */
- private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
- return isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId);
- }
-
- /**
* Returns "true" if access to the specified location provider is allowed by the specified
* user's settings. Access to all location providers is forbidden to non-location-provider
* processes belonging to background users.
@@ -1383,13 +1503,28 @@ public class LocationManagerService extends ILocationManager.Stub {
* @param userId the user id to query
*/
private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
- if (mEnabledProviders.contains(provider)) {
- return true;
+ if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ return isLocationEnabledForUser(userId);
}
- if (mDisabledProviders.contains(provider)) {
- return false;
+ if (LocationManager.FUSED_PROVIDER.equals(provider)) {
+ return isLocationEnabledForUser(userId);
+ }
+ synchronized (mLock) {
+ if (mMockProviders.containsKey(provider)) {
+ return isLocationEnabledForUser(userId);
+ }
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // Use system settings
+ ContentResolver cr = mContext.getContentResolver();
+ String allowedProviders = Settings.Secure.getStringForUser(
+ cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+ return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- return isLocationProviderEnabledForUser(provider, userId);
}
@@ -1481,9 +1616,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// network and fused providers are ok with COARSE or FINE
return RESOLUTION_LEVEL_COARSE;
} else {
- // mock providers
- LocationProviderInterface lp = mMockProviders.get(provider);
- if (lp != null) {
+ for (LocationProvider lp : mProviders) {
+ if (!lp.getName().equals(provider)) {
+ continue;
+ }
+
ProviderProperties properties = lp.getProperties();
if (properties != null) {
if (properties.mRequiresSatellite) {
@@ -1496,6 +1633,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
}
+
return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
}
@@ -1550,7 +1688,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
- switch(allowedResolutionLevel) {
+ switch (allowedResolutionLevel) {
case RESOLUTION_LEVEL_COARSE:
return AppOpsManager.OPSTR_COARSE_LOCATION;
case RESOLUTION_LEVEL_FINE:
@@ -1565,7 +1703,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- boolean reportLocationAccessNoThrow(
+ private boolean reportLocationAccessNoThrow(
int pid, int uid, String packageName, int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
@@ -1577,7 +1715,8 @@ public class LocationManagerService extends ILocationManager.Stub {
return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
}
- boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
+ private boolean checkLocationAccess(int pid, int uid, String packageName,
+ int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
@@ -1597,7 +1736,7 @@ public class LocationManagerService extends ILocationManager.Stub {
ArrayList<String> out;
synchronized (mLock) {
out = new ArrayList<>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
+ for (LocationProvider provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
@@ -1623,7 +1762,7 @@ public class LocationManagerService extends ILocationManager.Stub {
try {
synchronized (mLock) {
out = new ArrayList<>(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
+ for (LocationProvider provider : mProviders) {
String name = provider.getName();
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
@@ -1633,7 +1772,8 @@ public class LocationManagerService extends ILocationManager.Stub {
&& !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
continue;
}
- if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ if (criteria != null
+ && !android.location.LocationProvider.propertiesMeetCriteria(
name, provider.getProperties(), criteria)) {
continue;
}
@@ -1658,7 +1798,7 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
@Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- String result = null;
+ String result;
List<String> providers = getProviders(criteria, enabledOnly);
if (!providers.isEmpty()) {
@@ -1673,7 +1813,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return result;
}
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
return null;
}
@@ -1689,51 +1829,32 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
- boolean result = LocationProvider.propertiesMeetCriteria(
+ boolean result = android.location.LocationProvider.propertiesMeetCriteria(
p.getName(), p.getProperties(), criteria);
if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
return result;
}
- private void updateProvidersLocked() {
- boolean changesMade = false;
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- boolean isEnabled = p.isEnabled();
- String name = p.getName();
- boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
- if (isEnabled && !shouldBeEnabled) {
- updateProviderListenersLocked(name, false);
- // 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.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- changesMade = true;
- } else if (!isEnabled && shouldBeEnabled) {
- updateProviderListenersLocked(name, true);
- changesMade = true;
- }
+ private void updateProvidersSettingsLocked() {
+ for (LocationProvider p : mProviders) {
+ p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
}
- if (changesMade) {
- mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
- UserHandle.ALL);
- mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
- UserHandle.ALL);
- }
- }
- private void updateProviderListenersLocked(String provider, boolean enabled) {
- int listeners = 0;
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
+ UserHandle.ALL);
+ }
- LocationProviderInterface p = mProvidersByName.get(provider);
+ private void updateProviderListenersLocked(String provider) {
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
+ boolean enabled = p.isEnabled();
+
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1747,7 +1868,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
deadReceivers.add(record.mReceiver);
}
- listeners++;
}
}
}
@@ -1758,16 +1878,11 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- if (enabled) {
- p.enable();
- applyRequirementsLocked(provider);
- } else {
- p.disable();
- }
+ applyRequirementsLocked(provider);
}
private void applyRequirementsLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1779,10 +1894,10 @@ public class LocationManagerService extends ILocationManager.Stub {
resolver,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
- // initialize the low power mode to true and set to false if any of the records requires
- providerRequest.lowPowerMode = true;
- if (records != null) {
+ if (p.isEnabled() && records != null && !records.isEmpty()) {
+ // initialize the low power mode to true and set to false if any of the records requires
+ providerRequest.lowPowerMode = true;
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
if (checkLocationAccess(
@@ -1875,7 +1990,7 @@ public class LocationManagerService extends ILocationManager.Stub {
public String[] getBackgroundThrottlingWhitelist() {
synchronized (mLock) {
return mBackgroundThrottlePackageWhitelist.toArray(
- new String[mBackgroundThrottlePackageWhitelist.size()]);
+ new String[0]);
}
}
@@ -1922,17 +2037,17 @@ public class LocationManagerService extends ILocationManager.Stub {
private class UpdateRecord {
final String mProvider;
- final LocationRequest mRealRequest; // original request from client
+ private final LocationRequest mRealRequest; // original request from client
LocationRequest mRequest; // possibly throttled version of the request
- final Receiver mReceiver;
- boolean mIsForegroundUid;
- Location mLastFixBroadcast;
- long mLastStatusBroadcast;
+ private final Receiver mReceiver;
+ private boolean mIsForegroundUid;
+ private Location mLastFixBroadcast;
+ private long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+ private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
mProvider = provider;
mRealRequest = request;
mRequest = request;
@@ -1958,7 +2073,7 @@ public class LocationManagerService extends ILocationManager.Stub {
/**
* Method to be called when record changes foreground/background
*/
- void updateForeground(boolean isForeground){
+ private void updateForeground(boolean isForeground) {
mIsForegroundUid = isForeground;
mRequestStatistics.updateForeground(
mReceiver.mIdentity.mPackageName, mProvider, isForeground);
@@ -1967,7 +2082,7 @@ public class LocationManagerService extends ILocationManager.Stub {
/**
* Method to be called when a record will no longer be used.
*/
- void disposeLocked(boolean removeReceiver) {
+ private void disposeLocked(boolean removeReceiver) {
mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
// remove from mRecordsByProvider
@@ -1980,13 +2095,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// remove from Receiver#mUpdateRecords
HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
- if (receiverRecords != null) {
- receiverRecords.remove(this.mProvider);
+ receiverRecords.remove(this.mProvider);
- // and also remove the Receiver if it has no more update records
- if (receiverRecords.size() == 0) {
- removeUpdatesLocked(mReceiver);
- }
+ // and also remove the Receiver if it has no more update records
+ if (receiverRecords.size() == 0) {
+ removeUpdatesLocked(mReceiver);
}
}
@@ -2070,7 +2183,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private void checkPackageName(String packageName) {
if (packageName == null) {
- throw new SecurityException("invalid package name: " + packageName);
+ throw new SecurityException("invalid package name: " + null);
}
int uid = Binder.getCallingUid();
String[] packages = mPackageManager.getPackagesForUid(uid);
@@ -2085,7 +2198,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private void checkPendingIntent(PendingIntent intent) {
if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + intent);
+ throw new IllegalArgumentException("invalid pending intent: " + null);
}
}
@@ -2137,7 +2250,7 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2145,7 +2258,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
- int pid, int uid, String packageName) {
+ int uid, String packageName) {
// Figure out the provider. Either its explicitly request (legacy use cases), or
// use the fused provider
if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2154,7 +2267,7 @@ public class LocationManagerService extends ILocationManager.Stub {
throw new IllegalArgumentException("provider name must not be null");
}
- LocationProviderInterface provider = mProvidersByName.get(name);
+ LocationProvider provider = mProvidersByName.get(name);
if (provider == null) {
throw new IllegalArgumentException("provider doesn't exist: " + name);
}
@@ -2173,8 +2286,7 @@ public class LocationManagerService extends ILocationManager.Stub {
oldRecord.disposeLocked(false);
}
- boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid, mCurrentUserId);
- if (isProviderEnabled) {
+ if (provider.isEnabled()) {
applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
@@ -2194,10 +2306,8 @@ public class LocationManagerService extends ILocationManager.Stub {
final int uid = Binder.getCallingUid();
synchronized (mLock) {
- WorkSource workSource = null;
- boolean hideFromAppOps = false;
Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
+ packageName, null, false);
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
@@ -2236,22 +2346,12 @@ public class LocationManagerService extends ILocationManager.Stub {
// update provider
for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
- continue;
- }
-
applyRequirementsLocked(provider);
}
}
private void applyAllProviderRequirementsLocked() {
- for (LocationProviderInterface p : mProviders) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedByCurrentUserSettingsLocked(p.getName())) {
- continue;
- }
-
+ for (LocationProvider p : mProviders) {
applyRequirementsLocked(p.getName());
}
}
@@ -2291,7 +2391,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// or use the fused provider
String name = request.getProvider();
if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProviderInterface provider = mProvidersByName.get(name);
+ LocationProvider provider = mProvidersByName.get(name);
if (provider == null) return null;
if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
@@ -2314,7 +2414,7 @@ public class LocationManagerService extends ILocationManager.Stub {
location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
if ((locationAgeMs > mLastLocationMaxAgeMs)
&& (mAppOps.unsafeCheckOp(op, uid, packageName)
- == AppOpsManager.MODE_FOREGROUND)) {
+ == AppOpsManager.MODE_FOREGROUND)) {
return null;
}
@@ -2358,7 +2458,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
return false;
}
- LocationProviderInterface p = null;
+ LocationProvider p = null;
String provider = location.getProvider();
if (provider != null) {
p = mProvidersByName.get(provider);
@@ -2370,7 +2470,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return false;
}
synchronized (mLock) {
- if (!isAllowedByCurrentUserSettingsLocked(provider)) {
+ if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
if (D) {
Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
}
@@ -2444,31 +2544,20 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
-
@Override
public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) {
if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
return false;
}
- try {
- mGnssStatusProvider.registerGnssStatusCallback(callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "mGpsStatusProvider.registerGnssStatusCallback failed", e);
- return false;
- }
- return true;
+ // TODO(b/120449926): The GNSS status listeners should be handled similar to the GNSS
+ // measurements listeners.
+ return mGnssStatusProvider.addListener(callback, Binder.getCallingUid(), packageName);
}
@Override
public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
- synchronized (mLock) {
- try {
- mGnssStatusProvider.unregisterGnssStatusCallback(callback);
- } catch (Exception e) {
- Slog.e(TAG, "mGpsStatusProvider.unregisterGnssStatusCallback failed", e);
- }
- }
+ mGnssStatusProvider.removeListener(callback);
}
@Override
@@ -2481,13 +2570,15 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
Identity callerIdentity
= new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ // TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- return mGnssMeasurementsProvider.addListener(listener);
+ return mGnssMeasurementsProvider.addListener(listener,
+ callerIdentity.mUid, callerIdentity.mPackageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2499,11 +2590,13 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
- if (mGnssMeasurementsProvider != null) {
- synchronized (mLock) {
- mGnssMeasurementsListeners.remove(listener.asBinder());
- mGnssMeasurementsProvider.removeListener(listener);
- }
+ if (mGnssMeasurementsProvider == null) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mGnssMeasurementsListeners.remove(listener.asBinder());
+ mGnssMeasurementsProvider.removeListener(listener);
}
}
@@ -2518,13 +2611,15 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
Identity callerIdentity
= new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ // TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- return mGnssNavigationMessageProvider.addListener(listener);
+ return mGnssNavigationMessageProvider.addListener(listener,
+ callerIdentity.mUid, callerIdentity.mPackageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2560,10 +2655,11 @@ public class LocationManagerService extends ILocationManager.Stub {
}
synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return false;
- return p.sendExtraCommand(command, extras);
+ p.sendExtraCommand(command, extras);
+ return true;
}
}
@@ -2588,14 +2684,10 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
@Override
public ProviderProperties getProviderProperties(String provider) {
- if (mProvidersByName.get(provider) == null) {
- return null;
- }
-
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
- LocationProviderInterface p;
+ LocationProvider p;
synchronized (mLock) {
p = mProvidersByName.get(provider);
}
@@ -2611,25 +2703,25 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
@Override
public String getNetworkProviderPackage() {
- LocationProviderInterface p;
+ LocationProvider p;
synchronized (mLock) {
- if (mProvidersByName.get(LocationManager.NETWORK_PROVIDER) == null) {
- return null;
- }
p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
}
- if (p instanceof LocationProviderProxy) {
- return ((LocationProviderProxy) p).getConnectedPackageName();
+ if (p == null) {
+ return null;
+ }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
}
return null;
}
/**
- * Returns the current location enabled/disabled status for a user
+ * Returns the current location enabled/disabled status for a user
*
- * @param userId the id of the user
- * @return true if location is enabled
+ * @param userId the id of the user
+ * @return true if location is enabled
*/
@Override
public boolean isLocationEnabledForUser(int userId) {
@@ -2647,7 +2739,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return false;
}
final List<String> providerList = Arrays.asList(allowedProviders.split(","));
- for(String provider : mRealProviders.keySet()) {
+ for (String provider : mRealProviders.keySet()) {
if (provider.equals(LocationManager.PASSIVE_PROVIDER)
|| provider.equals(LocationManager.FUSED_PROVIDER)) {
continue;
@@ -2664,16 +2756,16 @@ public class LocationManagerService extends ILocationManager.Stub {
}
/**
- * Enable or disable location for a user
+ * Enable or disable location for a user
*
- * @param enabled true to enable location, false to disable location
- * @param userId the id of the user
+ * @param enabled true to enable location, false to disable location
+ * @param userId the id of the user
*/
@Override
public void setLocationEnabledForUser(boolean enabled, int userId) {
mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
+ android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Requires WRITE_SECURE_SETTINGS permission");
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
checkInteractAcrossUsersPermission(userId);
@@ -2688,7 +2780,7 @@ public class LocationManagerService extends ILocationManager.Stub {
allProvidersSet.addAll(allRealProviders);
// When disabling location, disable gps and network provider that could have been
// enabled by location mode api.
- if (enabled == false) {
+ if (!enabled) {
allProvidersSet.add(LocationManager.GPS_PROVIDER);
allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
}
@@ -2723,93 +2815,48 @@ public class LocationManagerService extends ILocationManager.Stub {
}
/**
- * Returns the current enabled/disabled status of a location provider and user
+ * Returns the current enabled/disabled status of a location provider and user
*
- * @param provider name of the provider
- * @param userId the id of the user
- * @return true if the provider exists and is enabled
+ * @param providerName name of the provider
+ * @param userId the id of the user
+ * @return true if the provider exists and is enabled
*/
@Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
+ public boolean isProviderEnabledForUser(String providerName, int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
checkInteractAcrossUsersPermission(userId);
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- int uid = Binder.getCallingUid();
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLocked(provider, uid, userId);
+ if (!isLocationEnabledForUser(userId)) {
+ return false;
}
- }
-
- /**
- * Enable or disable a single location provider.
- *
- * @param provider name of the provider
- * @param enabled true to enable the provider. False to disable the provider
- * @param userId the id of the user to set
- * @return true if the value was set, false on errors
- */
- @Override
- public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+ if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
long identity = Binder.clearCallingIdentity();
try {
+ LocationProvider provider;
synchronized (mLock) {
- // No such provider exists
- if (!mProvidersByName.containsKey(provider)) return false;
-
- // If it is a test provider, do not write to Settings.Secure
- if (mMockProviders.containsKey(provider)) {
- setTestProviderEnabled(provider, enabled);
- return true;
- }
-
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- String providerChange = (enabled ? "+" : "-") + provider;
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- providerChange, userId);
+ provider = mProvidersByName.get(providerName);
}
+ return provider != null && provider.isEnabled();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
- * Read location provider status from Settings.Secure
+ * Enable or disable a single location provider.
*
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
+ * @param provider name of the provider
+ * @param enabled true to enable the provider. False to disable the provider
+ * @param userId the id of the user to set
+ * @return true if the value was set, false on errors
*/
- private boolean isLocationProviderEnabledForUser(String provider, int userId) {
- long identity = Binder.clearCallingIdentity();
- try {
- // Use system settings
- ContentResolver cr = mContext.getContentResolver();
- String allowedProviders = Settings.Secure.getStringForUser(
- cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
- return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ @Override
+ public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
+ return false;
}
/**
@@ -2941,7 +2988,7 @@ public class LocationManagerService extends ILocationManager.Stub {
long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
- LocationProviderInterface p = mProvidersByName.get(provider);
+ LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
@@ -3053,7 +3100,7 @@ public class LocationManagerService extends ILocationManager.Stub {
LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
long prevStatusUpdateTime = r.mLastStatusBroadcast;
if ((newStatusUpdateTime > prevStatusUpdateTime)
- && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+ && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
@@ -3098,8 +3145,8 @@ public class LocationManagerService extends ILocationManager.Stub {
/**
* Updates last location with the given location
*
- * @param location new location to update
- * @param provider Location provider to update for
+ * @param location new location to update
+ * @param provider Location provider to update for
*/
private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
@@ -3154,13 +3201,11 @@ public class LocationManagerService extends ILocationManager.Stub {
}
synchronized (mLock) {
- if (isAllowedByCurrentUserSettingsLocked(provider)) {
- if (!passive) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(myLocation);
- }
- handleLocationChangedLocked(myLocation, passive);
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(myLocation);
}
+ handleLocationChangedLocked(myLocation, passive);
}
}
@@ -3245,13 +3290,12 @@ public class LocationManagerService extends ILocationManager.Stub {
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)
|| LocationManager.FUSED_PROVIDER.equals(name)) {
- LocationProviderInterface p = mProvidersByName.get(name);
+ LocationProvider p = mProvidersByName.get(name);
if (p != null) {
removeProviderLocked(p);
}
}
addTestProviderLocked(name, properties);
- updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
}
@@ -3260,9 +3304,12 @@ public class LocationManagerService extends ILocationManager.Stub {
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
- MockProvider provider = new MockProvider(name, this, properties);
+
+ LocationProvider provider = new LocationProvider(name);
+ MockProvider mockProvider = new MockProvider(provider, properties);
+
addProviderLocked(provider);
- mMockProviders.put(name, provider);
+ mMockProviders.put(name, mockProvider);
mLastLocation.put(name, null);
mLastLocationCoarseInterval.put(name, null);
}
@@ -3274,28 +3321,25 @@ public class LocationManagerService extends ILocationManager.Stub {
}
synchronized (mLock) {
-
- // These methods can't be called after removing the test provider, so first make sure
- // we don't leave anything dangling.
- clearTestProviderEnabled(provider, opPackageName);
- clearTestProviderLocation(provider, opPackageName);
-
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
+
long identity = Binder.clearCallingIdentity();
- removeProviderLocked(mProvidersByName.get(provider));
+ try {
+ removeProviderLocked(mProvidersByName.get(provider));
- // reinstate real provider if available
- LocationProviderInterface realProvider = mRealProviders.get(provider);
- if (realProvider != null) {
- addProviderLocked(realProvider);
+ // reinstate real provider if available
+ LocationProvider realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mLastLocation.put(provider, null);
- mLastLocationCoarseInterval.put(provider, null);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
}
}
@@ -3325,23 +3369,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
long identity = Binder.clearCallingIdentity();
- mockProvider.setLocation(mock);
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderLocation(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+ try {
+ mockProvider.setLocation(mock);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mockProvider.clearLocation();
}
}
@@ -3350,47 +3382,18 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- setTestProviderEnabled(provider, enabled);
- }
- /** Enable or disable a test location provider. */
- private void setTestProviderEnabled(String provider, boolean enabled) {
synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
long identity = Binder.clearCallingIdentity();
- if (enabled) {
- mockProvider.enable();
- mEnabledProviders.add(provider);
- mDisabledProviders.remove(provider);
- } else {
- mockProvider.disable();
- mEnabledProviders.remove(provider);
- mDisabledProviders.add(provider);
- }
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void clearTestProviderEnabled(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+ try {
+ mockProvider.setEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- long identity = Binder.clearCallingIdentity();
- mEnabledProviders.remove(provider);
- mDisabledProviders.remove(provider);
- updateProvidersLocked();
- Binder.restoreCallingIdentity(identity);
}
}
@@ -3450,10 +3453,11 @@ public class LocationManagerService extends ILocationManager.Stub {
+ identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
}
pw.println(" Overlay Provider Packages:");
- for (LocationProviderInterface provider : mProviders) {
- if (provider instanceof LocationProviderProxy) {
+ for (LocationProvider provider : mProviders) {
+ if (provider.mProvider instanceof LocationProviderProxy) {
pw.println(" " + provider.getName() + ": "
- + ((LocationProviderProxy) provider).getConnectedPackageName());
+ + ((LocationProviderProxy) provider.mProvider)
+ .getConnectedPackageName());
}
}
pw.println(" Historical Records by Provider:");
@@ -3479,25 +3483,12 @@ public class LocationManagerService extends ILocationManager.Stub {
mGeofenceManager.dump(pw);
- if (mEnabledProviders.size() > 0) {
- pw.println(" Enabled Providers:");
- for (String i : mEnabledProviders) {
- pw.println(" " + i);
- }
-
- }
- if (mDisabledProviders.size() > 0) {
- pw.println(" Disabled Providers:");
- for (String i : mDisabledProviders) {
- pw.println(" " + i);
- }
- }
pw.append(" ");
mBlacklist.dump(pw);
if (mMockProviders.size() > 0) {
pw.println(" Mock Providers:");
for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
- i.getValue().dump(pw, " ");
+ i.getValue().dump(fd, pw, args);
}
}
@@ -3514,13 +3505,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (args.length > 0 && "short".equals(args[0])) {
return;
}
- for (LocationProviderInterface provider : mProviders) {
- pw.print(provider.getName() + " Internal State");
- if (provider instanceof LocationProviderProxy) {
- LocationProviderProxy proxy = (LocationProviderProxy) provider;
- pw.print(" (" + proxy.getConnectedPackageName() + ")");
- }
- pw.println(":");
+ for (LocationProvider provider : mProviders) {
provider.dump(fd, pw, args);
}
if (mGnssBatchingInProgress) {
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index fa3babad639d..cee98c10c7f7 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -31,6 +31,7 @@ import android.text.format.DateFormat;
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.os.AppIdToPackageMap;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.CachedDeviceState;
import com.android.internal.os.LooperStats;
@@ -92,6 +93,7 @@ public class LooperStatsService extends Binder {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ AppIdToPackageMap packageMap = AppIdToPackageMap.getSnapshot();
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis()));
pw.print("On battery time (ms): ");
@@ -121,7 +123,7 @@ public class LooperStatsService extends Binder {
pw.println(header);
for (LooperStats.ExportedEntry entry : entries) {
pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
- entry.workSourceUid,
+ packageMap.mapUid(entry.workSourceUid),
entry.threadName,
entry.handlerClassName,
entry.messageName,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 87a42fa45143..4678fec377d0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -956,18 +956,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub
// INetworkManagementService members
//
@Override
- public INetd getNetdService() throws RemoteException {
- final CountDownLatch connectedSignal = mConnectedSignal;
- if (connectedSignal != null) {
- try {
- connectedSignal.await();
- } catch (InterruptedException ignored) {}
- }
-
- return mNetdService;
- }
-
- @Override
public String[] listInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 574f54a51eb1..d09823efb6fa 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,9 +16,7 @@
package com.android.server;
-import android.annotation.MainThread;
import android.annotation.Nullable;
-import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -168,13 +166,13 @@ public class ServiceWatcher implements ServiceConnection {
// called on handler thread
@GuardedBy("mBindLock")
protected void onBind() {
-
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
// called on handler thread
@GuardedBy("mBindLock")
protected void onUnbind() {
-
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
/**
@@ -205,12 +203,12 @@ public class ServiceWatcher implements ServiceConnection {
@Override
public void onPackageRemoved(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+ bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
}
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+ bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
return super.onPackageChanged(packageName, uid, components);
}
}.register(mContext, UserHandle.ALL, true, mHandler);
@@ -243,20 +241,16 @@ public class ServiceWatcher implements ServiceConnection {
return true;
}
- /** Returns thje name of the currently connected package or null. */
+ /** Returns the name of the currently connected package or null. */
@Nullable
public String getCurrentPackageName() {
ComponentName bestComponent = mBestComponent;
return bestComponent == null ? null : bestComponent.getPackageName();
}
- public int getCurrentPackageVersion() {
- return mBestVersion;
- }
-
/**
- * Runs the given BinderRunner if currently connected. Returns true if it was run, and false
- * otherwise. All invocations to runOnBinder are run serially.
+ * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
+ * serially.
*/
public final void runOnBinder(BinderRunner runner) {
synchronized (mBindLock) {
@@ -267,7 +261,7 @@ public class ServiceWatcher implements ServiceConnection {
} catch (Exception e) {
// remote exceptions cannot be allowed to crash system server
Log.e(TAG, "exception while while running " + runner + " on " + service
- + " from " + mBestComponent.toShortString(), e);
+ + " from " + this, e);
}
}
}
@@ -280,14 +274,6 @@ public class ServiceWatcher implements ServiceConnection {
UserHandle.USER_SYSTEM).isEmpty();
}
- /**
- * Searches and binds to the best package, or do nothing if the best package
- * is already bound, unless force rebinding is requested.
- *
- * @param forceRebind Force a rebinding to the best package if it's already
- * bound.
- */
- @WorkerThread
private void bindBestPackage(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -365,7 +351,6 @@ public class ServiceWatcher implements ServiceConnection {
}
}
- @WorkerThread
private void bind(ComponentName component, int version, int userId) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -382,7 +367,6 @@ public class ServiceWatcher implements ServiceConnection {
UserHandle.of(userId));
}
- @WorkerThread
private void unbind() {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
@@ -396,7 +380,6 @@ public class ServiceWatcher implements ServiceConnection {
mBestUserId = UserHandle.USER_NULL;
}
- @MainThread
@Override
public final void onServiceConnected(ComponentName component, IBinder binder) {
mHandler.post(() -> {
@@ -410,7 +393,6 @@ public class ServiceWatcher implements ServiceConnection {
});
}
- @MainThread
@Override
public final void onServiceDisconnected(ComponentName component) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a2cbfaa02bfb..b04ae1746d18 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -67,7 +67,9 @@ import com.android.server.am.BatteryStatsService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
/**
@@ -196,6 +198,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
+ private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
+
private int[] mSrvccState;
private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -229,8 +233,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
| PhoneStateListener.LISTEN_CELL_INFO;
static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
- PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
+ PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+ | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -357,6 +362,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCellInfo = new ArrayList<List<CellInfo>>();
mSrvccState = new int[numPhones];
mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>();
+ mEmergencyNumberList = new HashMap<>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -752,6 +758,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+ try {
+ r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
try {
r.callback.onPhoneCapabilityChanged(mPhoneCapability);
@@ -1665,10 +1678,30 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) {
- // TODO checkPermission, modify Listener constent documentation
- // TODO implement multisim emergency number list update in listener
- // TODO implement PhoneStateListenerTest
+ public void notifyEmergencyNumberList() {
+ if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
+ return;
+ }
+
+ synchronized (mRecords) {
+ mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList();
+
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)) {
+ try {
+ r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+ if (VDBG) {
+ log("notifyEmergencyNumberList: emergencyNumberList= "
+ + mEmergencyNumberList);
+ }
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
}
@@ -1710,6 +1743,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
pw.println("mRadioPowerState=" + mRadioPowerState);
+ pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 40da881c13fe..c9cab9852f83 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17532,8 +17532,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
- int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
- - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
+ final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
+ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
+ / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
if (numEmptyProcs > cachedProcessLimit) {
// If there are more empty processes than our limit on cached
@@ -17544,17 +17545,19 @@ public class ActivityManagerService extends IActivityManager.Stub
// instead of a gazillion empty processes.
numEmptyProcs = cachedProcessLimit;
}
- int emptyFactor = numEmptyProcs/numSlots;
+ int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
if (emptyFactor < 1) emptyFactor = 1;
- int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
+ int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
+ / numSlots;
if (cachedFactor < 1) cachedFactor = 1;
- int stepCached = 0;
- int stepEmpty = 0;
+ int stepCached = -1;
+ int stepEmpty = -1;
int numCached = 0;
int numCachedExtraGroup = 0;
int numEmpty = 0;
int numTrimming = 0;
int lastCachedGroup = 0;
+ int lastCachedGroupImportance = 0;
int lastCachedGroupUid = 0;
mNumNonCachedProcs = 0;
@@ -17563,9 +17566,10 @@ public class ActivityManagerService extends IActivityManager.Stub
// First update the OOM adjustment for each of the
// application processes based on their current state.
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
- int nextCachedAdj = curCachedAdj+1;
- int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
- int nextEmptyAdj = curEmptyAdj+2;
+ int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+ int curCachedImpAdj = 0;
+ int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
boolean retryCycles = false;
@@ -17590,53 +17594,74 @@ public class ActivityManagerService extends IActivityManager.Stub
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
- // This process is a cached process holding activities...
- // assign it the next cached value for that type, and then
- // step that cached level.
- app.setCurRawAdj(curCachedAdj);
- app.curAdj = app.modifyRawOomAdj(curCachedAdj);
- if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
- + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
- + ")");
- if (curCachedAdj != nextCachedAdj) {
+ // Figure out the next cached level, taking into account groups.
+ boolean inGroup = false;
+ if (app.connectionGroup != 0) {
+ if (lastCachedGroupUid == app.uid
+ && lastCachedGroup == app.connectionGroup) {
+ // This is in the same group as the last process, just tweak
+ // adjustment by importance.
+ if (app.connectionImportance > lastCachedGroupImportance) {
+ lastCachedGroupImportance = app.connectionImportance;
+ if (curCachedAdj < nextCachedAdj
+ && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
+ curCachedImpAdj++;
+ }
+ }
+ inGroup = true;
+ } else {
+ lastCachedGroupUid = app.uid;
+ lastCachedGroup = app.connectionGroup;
+ lastCachedGroupImportance = app.connectionImportance;
+ }
+ }
+ if (!inGroup && curCachedAdj != nextCachedAdj) {
stepCached++;
+ curCachedImpAdj = 0;
if (stepCached >= cachedFactor) {
stepCached = 0;
curCachedAdj = nextCachedAdj;
- nextCachedAdj += 2;
+ nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
+ // This process is a cached process holding activities...
+ // assign it the next cached value for that type, and then
+ // step that cached level.
+ app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+ app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+ if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+ + " curCachedImpAdj=" + curCachedImpAdj + ")");
break;
default:
- // For everything else, assign next empty cached process
- // level and bump that up. Note that this means that
- // long-running services that have dropped down to the
- // cached level will be treated as empty (since their process
- // state is still as a service), which is what we want.
- app.setCurRawAdj(curEmptyAdj);
- app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
- if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
- + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
- + ")");
+ // Figure out the next cached level.
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
- nextEmptyAdj += 2;
+ nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
+ // For everything else, assign next empty cached process
+ // level and bump that up. Note that this means that
+ // long-running services that have dropped down to the
+ // cached level will be treated as empty (since their process
+ // state is still as a service), which is what we want.
+ app.setCurRawAdj(curEmptyAdj);
+ app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+ if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+ + ")");
break;
}
}
-
-
}
}
@@ -17668,6 +17693,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ lastCachedGroup = lastCachedGroupUid = 0;
+
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
@@ -18935,7 +18962,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ProcessMemoryState processMemoryState =
new ProcessMemoryState(uid,
r.processName,
- r.maxAdj,
+ r.curAdj,
memoryStat.pgfault,
memoryStat.pgmajfault,
memoryStat.rssInBytes,
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8c39d75ea6a4..3a0899de75c3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1337,7 +1337,7 @@ public final class BroadcastQueue {
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
+ r.curComponent.getPackageName(), false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 8cf9f1e9ae4a..117984e10cf5 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -137,9 +137,13 @@ public final class ProcessList {
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
- static final int CACHED_APP_MAX_ADJ = 906;
+ static final int CACHED_APP_MAX_ADJ = 999;
static final int CACHED_APP_MIN_ADJ = 900;
+ // Number of levels we have available for different service connection group importance
+ // levels.
+ static final int CACHED_APP_IMPORTANCE_LEVELS = 5;
+
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 800;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3c14393ca740..d75601be23e3 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -944,10 +944,11 @@ public class Tethering extends BaseNetworkObserver {
public boolean hasTetherableConfiguration() {
final TetheringConfiguration cfg = mConfig;
final boolean hasDownstreamConfiguration =
- (cfg.tetherableUsbRegexs.length != 0) ||
- (cfg.tetherableWifiRegexs.length != 0) ||
- (cfg.tetherableBluetoothRegexs.length != 0);
- final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty();
+ (cfg.tetherableUsbRegexs.length != 0)
+ || (cfg.tetherableWifiRegexs.length != 0)
+ || (cfg.tetherableBluetoothRegexs.length != 0);
+ final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty()
+ || cfg.chooseUpstreamAutomatically;
return hasDownstreamConfiguration && hasUpstreamConfiguration;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b7ed2f9bd473..602aedbc2d00 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -206,45 +206,6 @@ public class Vpn {
// Handle of the user initiating VPN.
private final int mUserHandle;
- // Listen to package removal and change events (update/uninstall) for this user
- private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Uri data = intent.getData();
- final String packageName = data == null ? null : data.getSchemeSpecificPart();
- if (packageName == null) {
- return;
- }
-
- synchronized (Vpn.this) {
- // Avoid race where always-on package has been unset
- if (!packageName.equals(getAlwaysOnPackage())) {
- return;
- }
-
- final String action = intent.getAction();
- Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
- + packageName + " in user " + mUserHandle);
-
- switch(action) {
- case Intent.ACTION_PACKAGE_REPLACED:
- // Start vpn after app upgrade
- startAlwaysOnVpn();
- break;
- case Intent.ACTION_PACKAGE_REMOVED:
- final boolean isPackageRemoved = !intent.getBooleanExtra(
- Intent.EXTRA_REPLACING, false);
- if (isPackageRemoved) {
- setAlwaysOnPackage(null, false);
- }
- break;
- }
- }
- }
- };
-
- private boolean mIsPackageIntentReceiverRegistered = false;
-
public Vpn(Looper looper, Context context, INetworkManagementService netService,
@UserIdInt int userHandle) {
this(looper, context, netService, userHandle, new SystemServices(context));
@@ -500,7 +461,6 @@ public class Vpn {
// Prepare this app. The notification will update as a side-effect of updateState().
prepareInternal(packageName);
}
- maybeRegisterPackageChangeReceiverLocked(packageName);
setVpnForcedLocked(mLockdown);
return true;
}
@@ -509,31 +469,6 @@ public class Vpn {
return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
}
- private void unregisterPackageChangeReceiverLocked() {
- if (mIsPackageIntentReceiverRegistered) {
- mContext.unregisterReceiver(mPackageIntentReceiver);
- mIsPackageIntentReceiverRegistered = false;
- }
- }
-
- private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
- // Unregister IntentFilter listening for previous always-on package change
- unregisterPackageChangeReceiverLocked();
-
- if (!isNullOrLegacyVpn(packageName)) {
- mIsPackageIntentReceiverRegistered = true;
-
- IntentFilter intentFilter = new IntentFilter();
- // Protected intent can only be sent by system. No permission required in register.
- intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
- mContext.registerReceiverAsUser(
- mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
- }
- }
-
/**
* @return the package name of the VPN controller responsible for always-on VPN,
* or {@code null} if none is set or always-on VPN is controlled through
@@ -1302,7 +1237,6 @@ public class Vpn {
setLockdown(false);
mAlwaysOn = false;
- unregisterPackageChangeReceiverLocked();
// Quit any active connections
agentDisconnect();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index de0f29851da5..25ca27836aa7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1082,13 +1082,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
assertRunOnServiceThread();
if (!canStartArcUpdateAction(message.getSource(), true)) {
- if (getAvrDeviceInfo() == null) {
+ HdmiDeviceInfo avrDeviceInfo = getAvrDeviceInfo();
+ if (avrDeviceInfo == null) {
// AVR may not have been discovered yet. Delay the message processing.
mDelayedMessageBuffer.add(message);
return true;
}
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
- if (!isConnectedToArcPort(message.getSource())) {
+ if (!isConnectedToArcPort(avrDeviceInfo.getPhysicalAddress())) {
displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT);
}
return true;
diff --git a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
index 513a6a3eeec8..aaea45e4adbf 100644
--- a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -19,6 +19,7 @@ package com.android.server.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.os.IInterface;
import android.util.Slog;
import java.io.PrintWriter;
@@ -29,21 +30,21 @@ import java.util.ArrayList;
* bound.
*
* @param <S> the concrete remote service class
- *
+ * @param <I> the interface of the binder service
* @hide
*/
-public abstract class AbstractMultiplePendingRequestsRemoteService<
- S extends AbstractMultiplePendingRequestsRemoteService<S>>
- extends AbstractRemoteService<S> {
+public abstract class AbstractMultiplePendingRequestsRemoteService<S
+ extends AbstractMultiplePendingRequestsRemoteService<S, I>, I extends IInterface>
+ extends AbstractRemoteService<S, I> {
private final int mInitialCapacity;
- protected ArrayList<PendingRequest<S>> mPendingRequests;
+ protected ArrayList<PendingRequest<S, I>> mPendingRequests;
public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
@NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
- @NonNull VultureCallback callback, boolean bindInstantServiceAllowed, boolean verbose,
- int initialCapacity) {
+ @NonNull VultureCallback<S> callback, boolean bindInstantServiceAllowed,
+ boolean verbose, int initialCapacity) {
super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
verbose);
mInitialCapacity = initialCapacity;
@@ -84,7 +85,7 @@ public abstract class AbstractMultiplePendingRequestsRemoteService<
}
@Override // from AbstractRemoteService
- void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
if (mPendingRequests == null) {
mPendingRequests = new ArrayList<>(mInitialCapacity);
}
diff --git a/services/core/java/com/android/server/infra/AbstractRemoteService.java b/services/core/java/com/android/server/infra/AbstractRemoteService.java
index 67b3ecf4896e..41dcf89a0b04 100644
--- a/services/core/java/com/android/server/infra/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractRemoteService.java
@@ -54,13 +54,13 @@ import java.lang.ref.WeakReference;
* (no pun intended) example of how to use it.
*
* @param <S> the concrete remote service class
+ * @param <I> the interface of the binder service
*
* @hide
*/
//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
-public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
- implements DeathRecipient {
-
+public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> implements DeathRecipient {
private static final int MSG_UNBIND = 1;
protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
@@ -74,11 +74,11 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
private final Context mContext;
private final Intent mIntent;
- private final VultureCallback mVultureCallback;
+ private final VultureCallback<S> mVultureCallback;
private final int mUserId;
private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
private final boolean mBindInstantServiceAllowed;
- private IInterface mServiceInterface;
+ protected I mService;
private boolean mBinding;
private boolean mDestroyed;
@@ -87,19 +87,21 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
/**
* Callback called when the service dies.
+ *
+ * @param <T> service class
*/
- public interface VultureCallback {
+ public interface VultureCallback<T> {
/**
* Called when the service dies.
*
* @param service service that died!
*/
- void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service);
+ void onServiceDied(T service);
}
// NOTE: must be package-protected so this class is not extend outside
AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
- @NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
+ @NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback,
boolean bindInstantServiceAllowed, boolean verbose) {
mContext = context;
mVultureCallback = callback;
@@ -150,7 +152,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
* Gets the base Binder interface from the service.
*/
@NonNull
- protected abstract IInterface getServiceInterface(@NonNull IBinder service);
+ protected abstract I getServiceInterface(@NonNull IBinder service);
/**
* Defines How long after the last interaction with the service we would unbind.
@@ -183,12 +185,14 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
private void handleBinderDied() {
if (checkIfDestroyed()) return;
- if (mServiceInterface != null) {
- mServiceInterface.asBinder().unlinkToDeath(this, 0);
+ if (mService != null) {
+ mService.asBinder().unlinkToDeath(this, 0);
}
- mServiceInterface = null;
+ mService = null;
mServiceDied = true;
- mVultureCallback.onServiceDied(this);
+ @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
+ final S castService = (S) this;
+ mVultureCallback.onServiceDied(castService);
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -216,12 +220,35 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
pw.println();
}
- protected void scheduleRequest(@NonNull PendingRequest<S> pendingRequest) {
+ /**
+ * Schedules a "sync" request.
+ *
+ * <p>This request must be responded by the service somehow (typically using a callback),
+ * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
+ * service doesn't respond.
+ */
+ protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+ cancelScheduledUnbind();
mHandler.sendMessage(obtainMessage(
AbstractRemoteService::handlePendingRequest, this, pendingRequest));
}
- protected void cancelScheduledUnbind() {
+ /**
+ * Schedules an async request.
+ *
+ * <p>This request is not expecting a callback from the service, hence it's represented by
+ * a simple {@link Runnable}.
+ */
+ protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
+ cancelScheduledUnbind();
+ // TODO(b/117779333): fix generics below
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
+ mHandler.sendMessage(
+ obtainMessage(AbstractRemoteService::handlePendingRequest, this, asyncRequest));
+ }
+
+ private void cancelScheduledUnbind() {
mHandler.removeMessages(MSG_UNBIND);
}
@@ -244,7 +271,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
* Handles a request, either processing it right now when bound, or saving it to be handled when
* bound.
*/
- protected final void handlePendingRequest(@NonNull PendingRequest<S> pendingRequest) {
+ protected final void handlePendingRequest(@NonNull PendingRequest<S, I> pendingRequest) {
if (checkIfDestroyed() || mCompleted) return;
if (!handleIsBound()) {
@@ -263,10 +290,10 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
/**
* Defines what to do with a request that arrives while not bound to the service.
*/
- abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest);
+ abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest);
private boolean handleIsBound() {
- return mServiceInterface != null;
+ return mService != null;
}
private void handleEnsureBound() {
@@ -300,9 +327,9 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
mBinding = false;
if (handleIsBound()) {
handleOnConnectedStateChangedInternal(false);
- if (mServiceInterface != null) {
- mServiceInterface.asBinder().unlinkToDeath(this, 0);
- mServiceInterface = null;
+ if (mService != null) {
+ mService.asBinder().unlinkToDeath(this, 0);
+ mService = null;
}
}
mContext.unbindService(mServiceConnection);
@@ -318,7 +345,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
return;
}
mBinding = false;
- mServiceInterface = getServiceInterface(service);
+ mService = getServiceInterface(service);
try {
service.linkToDeath(AbstractRemoteService.this, 0);
} catch (RemoteException re) {
@@ -332,7 +359,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
@Override
public void onServiceDisconnected(ComponentName name) {
mBinding = true;
- mServiceInterface = null;
+ mService = null;
}
}
@@ -349,10 +376,15 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
/**
* Base class for the requests serviced by the remote service.
*
+ * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
+ * communicate back with the system server. For cases where that's not needed, you should use
+ * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+ *
* @param <S> the remote service class
+ * @param <I> the interface of the binder service
*/
- public abstract static class PendingRequest<S extends AbstractRemoteService<S>>
- implements Runnable {
+ public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> implements Runnable {
protected final String mTag = getClass().getSimpleName();
protected final Object mLock = new Object();
@@ -366,7 +398,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
@GuardedBy("mLock")
private boolean mCompleted;
- protected PendingRequest(S service) {
+ protected PendingRequest(@NonNull S service) {
mWeakService = new WeakReference<>(service);
mServiceHandler = service.mHandler;
mTimeoutTrigger = () -> {
@@ -452,4 +484,50 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
return false;
}
}
+
+ /**
+ * Represents a request that does not expect a callback from the remote service.
+ *
+ * @param <I> the interface of the binder service
+ */
+ public interface AsyncRequest<I extends IInterface> {
+
+ /**
+ * Run Forrest, run!
+ */
+ void run(@NonNull I binder) throws RemoteException;
+ }
+
+ private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>,
+ I extends IInterface> extends PendingRequest<S, I> {
+ private static final String TAG = MyAsyncPendingRequest.class.getSimpleName();
+
+ private final AsyncRequest<I> mRequest;
+
+ protected MyAsyncPendingRequest(@NonNull S service, @NonNull AsyncRequest<I> request) {
+ super(service);
+
+ mRequest = request;
+ }
+
+ @Override
+ public void run() {
+ final S remoteService = getService();
+ if (remoteService == null) return;
+ try {
+ mRequest.run(remoteService.mService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception handling async request (" + this + "): " + e);
+ } finally {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onTimeout(S remoteService) {
+ // TODO(b/117779333): should not happen because we called finish() on run(), although
+ // currently it might be called if the service is destroyed while showing it.
+ Slog.w(TAG, "AsyncPending requested timed out");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
index 37a1f5471f7b..d32f13b5d71b 100644
--- a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
@@ -19,6 +19,7 @@ package com.android.server.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.os.IInterface;
import android.util.Slog;
import java.io.PrintWriter;
@@ -29,17 +30,19 @@ import java.io.PrintWriter;
* <p>If another request is received while not bound, the previous one will be canceled.
*
* @param <S> the concrete remote service class
+ * @param <I> the interface of the binder service
*
* @hide
*/
-public abstract class AbstractSinglePendingRequestRemoteService<
- S extends AbstractSinglePendingRequestRemoteService<S>> extends AbstractRemoteService<S> {
+public abstract class AbstractSinglePendingRequestRemoteService<S
+ extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface>
+ extends AbstractRemoteService<S, I> {
- protected PendingRequest<S> mPendingRequest;
+ protected PendingRequest<S, I> mPendingRequest;
public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
@NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
- @NonNull VultureCallback callback, boolean bindInstantServiceAllowed,
+ @NonNull VultureCallback<S> callback, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
verbose);
@@ -48,7 +51,7 @@ public abstract class AbstractSinglePendingRequestRemoteService<
@Override // from AbstractRemoteService
void handlePendingRequests() {
if (mPendingRequest != null) {
- final PendingRequest<S> pendingRequest = mPendingRequest;
+ final PendingRequest<S, I> pendingRequest = mPendingRequest;
mPendingRequest = null;
handlePendingRequest(pendingRequest);
}
@@ -70,7 +73,7 @@ public abstract class AbstractSinglePendingRequestRemoteService<
}
@Override // from AbstractRemoteService
- void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
if (mPendingRequest != null) {
if (mVerbose) {
Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d96b6cba119b..e7c3c7bbe21b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,6 +1951,11 @@ public class InputManagerService extends IInputManager.Stub
}
// Native callback.
+ private int getPointerDisplayId() {
+ return mWindowManagerCallbacks.getPointerDisplayId();
+ }
+
+ // Native callback.
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -2017,6 +2022,8 @@ public class InputManagerService extends IInputManager.Stub
KeyEvent event, int policyFlags);
public int getPointerLayer();
+
+ public int getPointerDisplayId();
}
/**
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
new file mode 100644
index 000000000000..4c7c420214bd
--- /dev/null
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location;
+
+import android.location.Location;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * @hide
+ */
+public abstract class AbstractLocationProvider {
+
+ /**
+ * Interface for communicating from a location provider back to the location service.
+ */
+ public interface LocationProviderManager {
+
+ /**
+ * Called on location provider construction to make the location service aware of this
+ * provider and what it's initial enabled/disabled state should be.
+ */
+ void onAttachProvider(AbstractLocationProvider locationProvider, boolean initiallyEnabled);
+
+ /**
+ * May be called to inform the location service of a change in this location provider's
+ * enabled/disabled state.
+ */
+ void onSetEnabled(boolean enabled);
+
+ /**
+ * May be called to inform the location service of a change in this location provider's
+ * properties.
+ */
+ void onSetProperties(ProviderProperties properties);
+
+ /**
+ * May be called to inform the location service that this provider has a new location
+ * available.
+ */
+ void onReportLocation(Location location);
+
+ /**
+ * May be called to inform the location service that this provider has a new location
+ * available.
+ */
+ void onReportLocation(List<Location> locations);
+ }
+
+ private final LocationProviderManager mLocationProviderManager;
+
+ protected AbstractLocationProvider(LocationProviderManager locationProviderManager) {
+ this(locationProviderManager, true);
+ }
+
+ protected AbstractLocationProvider(LocationProviderManager locationProviderManager,
+ boolean initiallyEnabled) {
+ mLocationProviderManager = locationProviderManager;
+ mLocationProviderManager.onAttachProvider(this, initiallyEnabled);
+ }
+
+ /**
+ * Call this method to report a new location. May be called from any thread.
+ */
+ protected void reportLocation(Location location) {
+ mLocationProviderManager.onReportLocation(location);
+ }
+
+ /**
+ * Call this method to report a new location. May be called from any thread.
+ */
+ protected void reportLocation(List<Location> locations) {
+ mLocationProviderManager.onReportLocation(locations);
+ }
+
+ /**
+ * Call this method to report a change in provider enabled/disabled status. May be called from
+ * any thread.
+ */
+ protected void setEnabled(boolean enabled) {
+ mLocationProviderManager.onSetEnabled(enabled);
+ }
+
+ /**
+ * Call this method to report a change in provider properties. May be called from
+ * any thread.
+ */
+ protected void setProperties(ProviderProperties properties) {
+ mLocationProviderManager.onSetProperties(properties);
+ }
+
+ /**
+ * Called when the location service delivers a new request for fulfillment to the provider.
+ * Replaces any previous requests completely.
+ */
+ public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+ /**
+ * Called to dump debug or log information.
+ */
+ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ /**
+ * Retrieves the current status of the provider.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public int getStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
+
+ /**
+ * Retrieves the last update time of the status of the provider.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public long getStatusUpdateTime() {
+ return 0;
+ }
+
+ /** Sends a custom command to this provider. */
+ public abstract void sendExtraCommand(String command, Bundle extras);
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 330d1d5de1aa..29e1878b739a 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -31,10 +31,7 @@ import android.location.FusedBatchOptions;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
-import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
import android.location.IGpsGeofenceHardware;
-import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationListener;
@@ -84,6 +81,10 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -97,8 +98,18 @@ import java.util.Properties;
*
* {@hide}
*/
-public class GnssLocationProvider extends LocationProviderInterface
- implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends AbstractLocationProvider implements
+ InjectNtpTimeCallback,
+ GnssSatelliteBlacklistCallback {
+
+ /**
+ * Indicates that this method is a native entry point. Useful purely for IDEs which can
+ * understand entry points, and thus eliminate incorrect warnings about methods not used.
+ */
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface NativeEntryPoint {
+ }
private static final String TAG = "GnssLocationProvider";
@@ -249,7 +260,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
public void set(int svCount, int meanCn0, int maxCn0) {
- synchronized(this) {
+ synchronized (this) {
mSvCount = svCount;
mMeanCn0 = meanCn0;
mMaxCn0 = maxCn0;
@@ -258,7 +269,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
public void reset() {
- set(0,0,0);
+ set(0, 0, 0);
}
// Also used by outside methods to add to other bundles
@@ -314,7 +325,7 @@ public class GnssLocationProvider extends LocationProviderInterface
MAX_RETRY_INTERVAL);
// true if we are enabled, protected by this
- private boolean mEnabled;
+ private boolean mEnabled = true;
// states for injecting ntp and downloading xtra data
private static final int STATE_PENDING_NETWORK = 0;
@@ -328,9 +339,6 @@ public class GnssLocationProvider extends LocationProviderInterface
// true if GPS is navigating
private boolean mNavigating;
- // true if GPS engine is on
- private boolean mEngineOn;
-
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
@@ -380,9 +388,8 @@ public class GnssLocationProvider extends LocationProviderInterface
private boolean mSuplEsEnabled = false;
private final Context mContext;
- private final ILocationManager mILocationManager;
private final LocationExtras mLocationExtras = new LocationExtras();
- private final GnssStatusListenerHelper mListenerHelper;
+ private final GnssStatusListenerHelper mGnssStatusListenerHelper;
private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
@@ -443,20 +450,8 @@ public class GnssLocationProvider extends LocationProviderInterface
// GNSS Metrics
private GnssMetrics mGnssMetrics;
- private final IGnssStatusProvider mGnssStatusProvider = new IGnssStatusProvider.Stub() {
- @Override
- public void registerGnssStatusCallback(IGnssStatusListener callback) {
- mListenerHelper.addListener(callback);
- }
-
- @Override
- public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
- mListenerHelper.removeListener(callback);
- }
- };
-
- public IGnssStatusProvider getGnssStatusProvider() {
- return mGnssStatusProvider;
+ public GnssStatusListenerHelper getGnssStatusProvider() {
+ return mGnssStatusListenerHelper;
}
public IGpsGeofenceHardware getGpsGeofenceProxy() {
@@ -479,17 +474,22 @@ public class GnssLocationProvider extends LocationProviderInterface
return;
}
- if (action.equals(ALARM_WAKEUP)) {
- startNavigating(false);
- } else if (action.equals(ALARM_TIMEOUT)) {
- hibernate();
- } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
- || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)
- || Intent.ACTION_SCREEN_ON.equals(action)) {
- updateLowPowerMode();
- } else if (action.equals(SIM_STATE_CHANGED)) {
- subscriptionOrSimChanged(context);
+ switch (action) {
+ case ALARM_WAKEUP:
+ startNavigating(false);
+ break;
+ case ALARM_TIMEOUT:
+ hibernate();
+ break;
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+ case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ case Intent.ACTION_SCREEN_OFF:
+ case Intent.ACTION_SCREEN_ON:
+ updateLowPowerMode();
+ break;
+ case SIM_STATE_CHANGED:
+ subscriptionOrSimChanged(context);
+ break;
}
}
};
@@ -507,9 +507,7 @@ public class GnssLocationProvider extends LocationProviderInterface
*/
@Override
public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
- mHandler.post(()->{
- native_set_satellite_blacklist(constellations, svids);
- });
+ mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
}
private void subscriptionOrSimChanged(Context context) {
@@ -572,7 +570,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
interface SetCarrierProperty {
- public boolean set(int value);
+ boolean set(int value);
}
private void reloadGpsProperties(Context context, Properties properties) {
@@ -587,7 +585,7 @@ public class GnssLocationProvider extends LocationProviderInterface
/*
* Overlay carrier properties from a debug configuration file.
*/
- loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
+ loadPropertiesFromFile(properties);
// TODO: we should get rid of C2K specific setting.
setSuplHostPort(properties.getProperty("SUPL_HOST"),
properties.getProperty("SUPL_PORT"));
@@ -603,15 +601,15 @@ public class GnssLocationProvider extends LocationProviderInterface
if (native_is_gnss_configuration_supported()) {
Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
{
- put("SUPL_VER", (val) -> native_set_supl_version(val));
- put("SUPL_MODE", (val) -> native_set_supl_mode(val));
- put("SUPL_ES", (val) -> native_set_supl_es(val));
- put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
+ put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
+ put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
+ put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
+ put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
put("A_GLONASS_POS_PROTOCOL_SELECT",
- (val) -> native_set_gnss_pos_protocol_select(val));
+ GnssLocationProvider::native_set_gnss_pos_protocol_select);
put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
- (val) -> native_set_emergency_supl_pdn(val));
- put("GPS_LOCK", (val) -> native_set_gps_lock(val));
+ GnssLocationProvider::native_set_emergency_supl_pdn);
+ put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
}
};
@@ -622,7 +620,7 @@ public class GnssLocationProvider extends LocationProviderInterface
try {
int propertyValueInt = Integer.decode(propertyValueString);
boolean result = entry.getValue().set(propertyValueInt);
- if (result == false) {
+ if (!result) {
Log.e(TAG, "Unable to set " + propertyName);
}
} catch (NumberFormatException e) {
@@ -664,10 +662,9 @@ public class GnssLocationProvider extends LocationProviderInterface
}
}
- private boolean loadPropertiesFromFile(String filename,
- Properties properties) {
+ private void loadPropertiesFromFile(Properties properties) {
try {
- File file = new File(filename);
+ File file = new File(DEBUG_PROPERTIES_FILE);
FileInputStream stream = null;
try {
stream = new FileInputStream(file);
@@ -677,16 +674,15 @@ public class GnssLocationProvider extends LocationProviderInterface
}
} catch (IOException e) {
- if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename);
- return false;
+ if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
}
- return true;
}
- public GnssLocationProvider(Context context, ILocationManager ilocationManager,
+ public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
Looper looper) {
+ super(locationProviderManager, true);
+
mContext = context;
- mILocationManager = ilocationManager;
// Create a wake lock
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -730,7 +726,7 @@ public class GnssLocationProvider extends LocationProviderInterface
mNetInitiatedListener,
mSuplEsEnabled);
- mListenerHelper = new GnssStatusListenerHelper(mHandler) {
+ mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
@Override
protected boolean isAvailableInPlatform() {
return isSupported();
@@ -749,7 +745,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
};
- mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mHandler) {
+ mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) {
@Override
protected boolean isGpsEnabled() {
return isEnabled();
@@ -763,19 +759,20 @@ public class GnssLocationProvider extends LocationProviderInterface
mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
mGnssBatchingProvider = new GnssBatchingProvider();
mGnssGeofenceProvider = new GnssGeofenceProvider();
- }
- /**
- * Returns the name of this provider.
- */
- @Override
- public String getName() {
- return LocationManager.GPS_PROVIDER;
- }
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getSendingUserId() == UserHandle.USER_ALL) {
+ mEnabled = false;
+ handleDisable();
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, mHandler);
- @Override
- public ProviderProperties getProperties() {
- return PROPERTIES;
+ setProperties(PROPERTIES);
}
/**
@@ -840,9 +837,9 @@ public class GnssLocationProvider extends LocationProviderInterface
locationManager.requestLocationUpdates(provider,
LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
locationListener, mHandler.getLooper());
- locationListener.numLocationUpdateRequest++;
+ locationListener.mNumLocationUpdateRequest++;
mHandler.postDelayed(() -> {
- if (--locationListener.numLocationUpdateRequest == 0) {
+ if (--locationListener.mNumLocationUpdateRequest == 0) {
Log.i(TAG,
String.format("Removing location updates from %s provider.", provider));
locationManager.removeUpdates(locationListener);
@@ -905,43 +902,40 @@ public class GnssLocationProvider extends LocationProviderInterface
// hold wake lock while task runs
mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
- AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
- @Override
- public void run() {
- GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
- byte[] data = xtraDownloader.downloadXtraData();
- if (data != null) {
- if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
- native_inject_xtra_data(data, data.length);
- mXtraBackOff.reset();
- }
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
+ native_inject_xtra_data(data, data.length);
+ mXtraBackOff.reset();
+ }
- sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
+ sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
- if (data == null) {
- // try again later
- // since this is delayed and not urgent we do not hold a wake lock here
- mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
- mXtraBackOff.nextBackoffMillis());
- }
+ if (data == null) {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
+ mXtraBackOff.nextBackoffMillis());
+ }
- // Release wake lock held by task, synchronize on mLock in case multiple
- // download tasks overrun.
- synchronized (mLock) {
- if (mDownloadXtraWakeLock.isHeld()) {
- // This wakelock may have time-out, if a timeout was specified.
- // Catch (and ignore) any timeout exceptions.
- try {
- mDownloadXtraWakeLock.release();
- if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
- } catch (Exception e) {
- Log.i(TAG, "Wakelock timeout & release race exception in "
- + "handleDownloadXtraData()", e);
- }
- } else {
- Log.e(TAG, "WakeLock expired before release in "
- + "handleDownloadXtraData()");
+ // Release wake lock held by task, synchronize on mLock in case multiple
+ // download tasks overrun.
+ synchronized (mLock) {
+ if (mDownloadXtraWakeLock.isHeld()) {
+ // This wakelock may have time-out, if a timeout was specified.
+ // Catch (and ignore) any timeout exceptions.
+ try {
+ mDownloadXtraWakeLock.release();
+ if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+ } catch (Exception e) {
+ Log.i(TAG, "Wakelock timeout & release race exception in "
+ + "handleDownloadXtraData()", e);
}
+ } else {
+ Log.e(TAG, "WakeLock expired before release in "
+ + "handleDownloadXtraData()");
}
}
});
@@ -954,21 +948,6 @@ public class GnssLocationProvider extends LocationProviderInterface
}
}
- /**
- * Enables this provider. When enabled, calls to getStatus()
- * must be handled. Hardware may be started up
- * when the provider is enabled.
- */
- @Override
- public void enable() {
- synchronized (mLock) {
- if (mEnabled) return;
- mEnabled = true;
- }
-
- sendMessage(ENABLE, 1, null);
- }
-
private void setSuplHostPort(String hostString, String portString) {
if (hostString != null) {
mSuplServerHost = hostString;
@@ -1052,21 +1031,6 @@ public class GnssLocationProvider extends LocationProviderInterface
}
}
- /**
- * Disables this provider. When disabled, calls to getStatus()
- * need not be handled. Hardware may be shut
- * down while the provider is disabled.
- */
- @Override
- public void disable() {
- synchronized (mLock) {
- if (!mEnabled) return;
- mEnabled = false;
- }
-
- sendMessage(ENABLE, 0, null);
- }
-
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
@@ -1083,7 +1047,6 @@ public class GnssLocationProvider extends LocationProviderInterface
mGnssNavigationMessageProvider.onGpsEnabledChanged();
}
- @Override
public boolean isEnabled() {
synchronized (mLock) {
return mEnabled;
@@ -1147,7 +1110,7 @@ public class GnssLocationProvider extends LocationProviderInterface
updateClientUids(mWorkSource);
mFixInterval = (int) mProviderRequest.interval;
- mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.lowPowerMode;
// check for overflow
if (mFixInterval != mProviderRequest.interval) {
Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
@@ -1171,7 +1134,8 @@ public class GnssLocationProvider extends LocationProviderInterface
// set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
// and our fix interval is not short
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent); }
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+ }
}
} else {
updateClientUids(new WorkSource());
@@ -1220,16 +1184,14 @@ public class GnssLocationProvider extends LocationProviderInterface
List<WorkChain> goneChains = diffs[1];
if (newChains != null) {
- for (int i = 0; i < newChains.size(); ++i) {
- final WorkChain newChain = newChains.get(i);
+ for (WorkChain newChain : newChains) {
mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
newChain.getAttributionTag());
}
}
if (goneChains != null) {
- for (int i = 0; i < goneChains.size(); i++) {
- final WorkChain goneChain = goneChains.get(i);
+ for (WorkChain goneChain : goneChains) {
mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
goneChain.getAttributionTag());
}
@@ -1262,32 +1224,27 @@ public class GnssLocationProvider extends LocationProviderInterface
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
+ public void sendExtraCommand(String command, Bundle extras) {
long identity = Binder.clearCallingIdentity();
try {
- boolean result = false;
-
if ("delete_aiding_data".equals(command)) {
- result = deleteAidingData(extras);
+ deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
requestUtcTime();
- result = true;
} else if ("force_xtra_injection".equals(command)) {
if (mSupportsXtra) {
xtraDownloadRequest();
- result = true;
}
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
- return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- private boolean deleteAidingData(Bundle extras) {
+ private void deleteAidingData(Bundle extras) {
int flags;
if (extras == null) {
@@ -1311,10 +1268,7 @@ public class GnssLocationProvider extends LocationProviderInterface
if (flags != 0) {
native_delete_aiding_data(flags);
- return true;
}
-
- return false;
}
private void startNavigating(boolean singleShot) {
@@ -1358,7 +1312,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
- mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.lowPowerMode;
if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
interval, 0, 0, mLowPowerMode)) {
mStarted = false;
@@ -1415,10 +1369,7 @@ public class GnssLocationProvider extends LocationProviderInterface
return ((mEngineCapabilities & capability) != 0);
}
-
- /**
- * called from native code to update our position.
- */
+ @NativeEntryPoint
private void reportLocation(boolean hasLatLong, Location location) {
sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
}
@@ -1444,11 +1395,7 @@ public class GnssLocationProvider extends LocationProviderInterface
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
location.setExtras(mLocationExtras.getBundle());
- try {
- mILocationManager.reportLocation(location, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
+ reportLocation(location);
if (mStarted) {
mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1473,7 +1420,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
// notify status listeners
- mListenerHelper.onFirstFix(mTimeToFirstFix);
+ mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
}
if (mSingleShot) {
@@ -1482,7 +1429,8 @@ public class GnssLocationProvider extends LocationProviderInterface
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// For devices that use framework scheduling, a timer may be set to ensure we don't
- // spend too much power searching for a location, when the requested update rate is slow.
+ // spend too much power searching for a location, when the requested update rate is
+ // slow.
// As we just recievied a location, we'll cancel that timer.
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
mAlarmManager.cancel(mTimeoutIntent);
@@ -1502,9 +1450,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
}
- /**
- * called from native code to update our status
- */
+ @NativeEntryPoint
private void reportStatus(int status) {
if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
@@ -1512,22 +1458,19 @@ public class GnssLocationProvider extends LocationProviderInterface
switch (status) {
case GPS_STATUS_SESSION_BEGIN:
mNavigating = true;
- mEngineOn = true;
break;
case GPS_STATUS_SESSION_END:
mNavigating = false;
break;
case GPS_STATUS_ENGINE_ON:
- mEngineOn = true;
break;
case GPS_STATUS_ENGINE_OFF:
- mEngineOn = false;
mNavigating = false;
break;
}
if (wasNavigating != mNavigating) {
- mListenerHelper.onStatusChanged(mNavigating);
+ mGnssStatusListenerHelper.onStatusChanged(mNavigating);
// send an intent to notify that the GPS has been enabled or disabled
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -1538,17 +1481,15 @@ public class GnssLocationProvider extends LocationProviderInterface
// Helper class to carry data to handler for reportSvStatus
private static class SvStatusInfo {
- public int mSvCount;
- public int[] mSvidWithFlags;
- public float[] mCn0s;
- public float[] mSvElevations;
- public float[] mSvAzimuths;
- public float[] mSvCarrierFreqs;
+ private int mSvCount;
+ private int[] mSvidWithFlags;
+ private float[] mCn0s;
+ private float[] mSvElevations;
+ private float[] mSvAzimuths;
+ private float[] mSvCarrierFreqs;
}
- /**
- * called from native code to update SV info
- */
+ @NativeEntryPoint
private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
SvStatusInfo svStatusInfo = new SvStatusInfo();
@@ -1563,7 +1504,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
private void handleReportSvStatus(SvStatusInfo info) {
- mListenerHelper.onSvStatusChanged(
+ mGnssStatusListenerHelper.onSvStatusChanged(
info.mSvCount,
info.mSvidWithFlags,
info.mCn0s,
@@ -1622,75 +1563,52 @@ public class GnssLocationProvider extends LocationProviderInterface
}
}
- /**
- * called from native code to update AGPS status
- */
+ @NativeEntryPoint
private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
}
- /**
- * called from native code to report NMEA data received
- */
+ @NativeEntryPoint
private void reportNmea(long timestamp) {
if (!mItarSpeedLimitExceeded) {
int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
- mListenerHelper.onNmeaReceived(timestamp, nmea);
+ mGnssStatusListenerHelper.onNmeaReceived(timestamp, nmea);
}
}
- /**
- * called from native code - GNSS measurements callback
- */
+ @NativeEntryPoint
private void reportMeasurementData(GnssMeasurementsEvent event) {
if (!mItarSpeedLimitExceeded) {
// send to handler to allow native to return quickly
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGnssMeasurementsProvider.onMeasurementsAvailable(event);
- }
- });
+ mHandler.post(() -> mGnssMeasurementsProvider.onMeasurementsAvailable(event));
}
}
- /**
- * called from native code - GNSS navigation message callback
- */
+ @NativeEntryPoint
private void reportNavigationMessage(GnssNavigationMessage event) {
if (!mItarSpeedLimitExceeded) {
// send to handler to allow native to return quickly
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
- }
- });
+ mHandler.post(() -> mGnssNavigationMessageProvider.onNavigationMessageAvailable(event));
}
}
- /**
- * called from native code to inform us what the GPS engine capabilities are
- */
+ @NativeEntryPoint
private void setEngineCapabilities(final int capabilities) {
// send to handler thread for fast native return, and in-order handling
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mEngineCapabilities = capabilities;
-
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
- mNtpTimeHelper.enablePeriodicTimeInjection();
- requestUtcTime();
- }
+ mHandler.post(() -> {
+ mEngineCapabilities = capabilities;
- mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_MEASUREMENTS));
- mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_NAV_MESSAGES));
- restartRequests();
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+ mNtpTimeHelper.enablePeriodicTimeInjection();
+ requestUtcTime();
}
+
+ mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_MEASUREMENTS));
+ mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_NAV_MESSAGES));
+ restartRequests();
});
}
@@ -1710,27 +1628,21 @@ public class GnssLocationProvider extends LocationProviderInterface
updateRequirements();
}
- /**
- * Called from native code to inform us the hardware year.
- */
+ @NativeEntryPoint
private void setGnssYearOfHardware(final int yearOfHardware) {
// mHardwareYear is simply set here, to be read elsewhere, and is volatile for safe sync
if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
mHardwareYear = yearOfHardware;
}
- /**
- * Called from native code to inform us the hardware model name.
- */
+ @NativeEntryPoint
private void setGnssHardwareModelName(final String modelName) {
// mHardwareModelName is simply set here, to be read elsewhere, and volatile for safe sync
if (DEBUG) Log.d(TAG, "setGnssModelName called with " + modelName);
mHardwareModelName = modelName;
}
- /**
- * Called from native code to inform us GNSS HAL service died.
- */
+ @NativeEntryPoint
private void reportGnssServiceDied() {
if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
mHandler.post(() -> {
@@ -1750,6 +1662,7 @@ public class GnssLocationProvider extends LocationProviderInterface
* Returns the year of underlying GPS hardware.
*/
int getGnssYearOfHardware();
+
/**
* Returns the model name of underlying GPS hardware.
*/
@@ -1765,6 +1678,7 @@ public class GnssLocationProvider extends LocationProviderInterface
public int getGnssYearOfHardware() {
return mHardwareYear;
}
+
@Override
public String getGnssHardwareModelName() {
return mHardwareModelName;
@@ -1790,32 +1704,19 @@ public class GnssLocationProvider extends LocationProviderInterface
* @hide
*/
public GnssMetricsProvider getGnssMetricsProvider() {
- return new GnssMetricsProvider() {
- @Override
- public String getGnssMetricsAsProtoString() {
- return mGnssMetrics.dumpGnssMetricsAsProtoString();
- }
- };
+ return () -> mGnssMetrics.dumpGnssMetricsAsProtoString();
}
- /**
- * called from native code - GNSS location batch callback
- */
+ @NativeEntryPoint
private void reportLocationBatch(Location[] locationArray) {
List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
if (DEBUG) {
Log.d(TAG, "Location batch of size " + locationArray.length + " reported");
}
- try {
- mILocationManager.reportLocationBatch(locations);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocationBatch");
- }
+ reportLocation(locations);
}
- /**
- * called from native code to request XTRA data
- */
+ @NativeEntryPoint
private void xtraDownloadRequest() {
if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
@@ -1843,10 +1744,7 @@ public class GnssLocationProvider extends LocationProviderInterface
}
}
- /**
- * Called from native to report GPS Geofence transition
- * All geofence callbacks are called on the same thread
- */
+ @NativeEntryPoint
private void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
mHandler.post(() -> {
@@ -1864,9 +1762,7 @@ public class GnssLocationProvider extends LocationProviderInterface
});
}
- /**
- * called from native code to report GPS status change.
- */
+ @NativeEntryPoint
private void reportGeofenceStatus(int status, Location location) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1884,9 +1780,7 @@ public class GnssLocationProvider extends LocationProviderInterface
});
}
- /**
- * called from native code - Geofence Add callback
- */
+ @NativeEntryPoint
private void reportGeofenceAddStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1896,9 +1790,7 @@ public class GnssLocationProvider extends LocationProviderInterface
});
}
- /**
- * called from native code - Geofence Remove callback
- */
+ @NativeEntryPoint
private void reportGeofenceRemoveStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1908,9 +1800,7 @@ public class GnssLocationProvider extends LocationProviderInterface
});
}
- /**
- * called from native code - Geofence Pause callback
- */
+ @NativeEntryPoint
private void reportGeofencePauseStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1920,9 +1810,7 @@ public class GnssLocationProvider extends LocationProviderInterface
});
}
- /**
- * called from native code - Geofence Resume callback
- */
+ @NativeEntryPoint
private void reportGeofenceResumeStatus(int geofenceId, int status) {
mHandler.post(() -> {
if (mGeofenceHardwareImpl == null) {
@@ -1954,7 +1842,8 @@ public class GnssLocationProvider extends LocationProviderInterface
return mNetInitiatedListener;
}
- // Called by JNI function to report an NI request.
+ /** Reports a NI notification. */
+ @NativeEntryPoint
public void reportNiNotification(
int notificationId,
int niType,
@@ -1997,11 +1886,10 @@ public class GnssLocationProvider extends LocationProviderInterface
}
/**
- * Called from native code to request set id info.
* We should be careful about receiving null string from the TelephonyManager,
* because sending null String to JNI function would cause a crash.
*/
-
+ @NativeEntryPoint
private void requestSetID(int flags) {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2030,9 +1918,7 @@ public class GnssLocationProvider extends LocationProviderInterface
native_agps_set_id(type, data);
}
- /**
- * Called from native code to request location info.
- */
+ @NativeEntryPoint
private void requestLocation(boolean independentFromGnss) {
if (DEBUG) {
Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss);
@@ -2040,17 +1926,13 @@ public class GnssLocationProvider extends LocationProviderInterface
sendMessage(REQUEST_LOCATION, 0, independentFromGnss);
}
- /**
- * Called from native code to request utc time info
- */
+ @NativeEntryPoint
private void requestUtcTime() {
if (DEBUG) Log.d(TAG, "utcTimeRequest");
sendMessage(INJECT_NTP_TIME, 0, null);
}
- /**
- * Called from native code to request reference location info
- */
+ @NativeEntryPoint
private void requestRefLocation() {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2104,11 +1986,7 @@ public class GnssLocationProvider extends LocationProviderInterface
int message = msg.what;
switch (message) {
case ENABLE:
- if (msg.arg1 == 1) {
- handleEnable();
- } else {
- handleDisable();
- }
+ handleEnable();
break;
case SET_REQUEST:
GpsRequest gpsRequest = (GpsRequest) msg.obj;
@@ -2153,7 +2031,8 @@ public class GnssLocationProvider extends LocationProviderInterface
}
/**
- * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
+ * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
+ * Looper)}.
* It is in charge of loading properties and registering for events that will be posted to
* this handler.
*/
@@ -2206,12 +2085,11 @@ public class GnssLocationProvider extends LocationProviderInterface
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
long minTime = 0;
float minDistance = 0;
- boolean oneShot = false;
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
LocationManager.PASSIVE_PROVIDER,
minTime,
minDistance,
- oneShot);
+ false);
// Don't keep track of this request since it's done on behalf of other clients
// (which are kept track of separately).
request.setHideFromAppOps(true);
@@ -2219,11 +2097,14 @@ public class GnssLocationProvider extends LocationProviderInterface
request,
new NetworkLocationListener(),
getLooper());
+
+ // enable gps provider, it will never be disabled (legacy behavior)
+ sendEmptyMessage(ENABLE);
}
}
private abstract class LocationChangeListener implements LocationListener {
- int numLocationUpdateRequest;
+ private int mNumLocationUpdateRequest;
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 0add86315487..3e2ba87a033e 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -38,7 +38,6 @@ public abstract class GnssMeasurementsProvider extends
private static final String TAG = "GnssMeasurementsProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final Context mContext;
private final GnssMeasurementProviderNative mNative;
private boolean mIsCollectionStarted;
@@ -51,8 +50,7 @@ public abstract class GnssMeasurementsProvider extends
@VisibleForTesting
GnssMeasurementsProvider(Context context, Handler handler,
GnssMeasurementProviderNative aNative) {
- super(handler, TAG);
- mContext = context;
+ super(context, handler, TAG);
mNative = aNative;
}
@@ -98,9 +96,13 @@ public abstract class GnssMeasurementsProvider extends
}
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
- ListenerOperation<IGnssMeasurementsListener> operation =
- listener -> listener.onGnssMeasurementsReceived(event);
- foreach(operation);
+ foreach((IGnssMeasurementsListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(TAG, packageName, "GNSS measurements");
+ return;
+ }
+ listener.onGnssMeasurementsReceived(event);
+ });
}
public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) {
@@ -149,7 +151,8 @@ public abstract class GnssMeasurementsProvider extends
}
@Override
- public void execute(IGnssMeasurementsListener listener) throws RemoteException {
+ public void execute(IGnssMeasurementsListener listener,
+ int uid, String packageName) throws RemoteException {
listener.onStatusChanged(mStatus);
}
}
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index 1b4fd187051a..679919f8fc67 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.content.Context;
import android.location.GnssNavigationMessage;
import android.location.IGnssNavigationMessageListener;
import android.os.Handler;
@@ -39,13 +40,14 @@ public abstract class GnssNavigationMessageProvider
private final GnssNavigationMessageProviderNative mNative;
private boolean mCollectionStarted;
- protected GnssNavigationMessageProvider(Handler handler) {
- this(handler, new GnssNavigationMessageProviderNative());
+ protected GnssNavigationMessageProvider(Context context, Handler handler) {
+ this(context, handler, new GnssNavigationMessageProviderNative());
}
@VisibleForTesting
- GnssNavigationMessageProvider(Handler handler, GnssNavigationMessageProviderNative aNative) {
- super(handler, TAG);
+ GnssNavigationMessageProvider(Context context, Handler handler,
+ GnssNavigationMessageProviderNative aNative) {
+ super(context, handler, TAG);
mNative = aNative;
}
@@ -84,15 +86,10 @@ public abstract class GnssNavigationMessageProvider
}
public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
- ListenerOperation<IGnssNavigationMessageListener> operation =
- new ListenerOperation<IGnssNavigationMessageListener>() {
- @Override
- public void execute(IGnssNavigationMessageListener listener)
- throws RemoteException {
- listener.onGnssNavigationMessageReceived(event);
- }
- };
- foreach(operation);
+ foreach((IGnssNavigationMessageListener listener, int uid, String packageName) -> {
+ listener.onGnssNavigationMessageReceived(event);
+ }
+ );
}
public void onCapabilitiesUpdated(boolean isGnssNavigationMessageSupported) {
@@ -138,7 +135,8 @@ public abstract class GnssNavigationMessageProvider
}
@Override
- public void execute(IGnssNavigationMessageListener listener) throws RemoteException {
+ public void execute(IGnssNavigationMessageListener listener,
+ int uid, String packageName) throws RemoteException {
listener.onStatusChanged(mStatus);
}
}
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 124220f17f1f..454dbddc406a 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -16,16 +16,20 @@
package com.android.server.location;
+import android.content.Context;
import android.location.IGnssStatusListener;
import android.os.Handler;
-import android.os.RemoteException;
+import android.util.Log;
/**
* Implementation of a handler for {@link IGnssStatusListener}.
*/
-abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
- protected GnssStatusListenerHelper(Handler handler) {
- super(handler, "GnssStatusListenerHelper");
+public abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
+ private static final String TAG = "GnssStatusListenerHelper";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ protected GnssStatusListenerHelper(Context context, Handler handler) {
+ super(context, handler, TAG);
setSupported(GnssLocationProvider.isSupported());
}
@@ -43,33 +47,22 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus
}
public void onStatusChanged(boolean isNavigating) {
- Operation operation;
if (isNavigating) {
- operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onGnssStarted();
- }
- };
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ listener.onGnssStarted();
+ });
} else {
- operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onGnssStopped();
- }
- };
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ listener.onGnssStopped();
+ });
}
- foreach(operation);
}
public void onFirstFix(final int timeToFirstFix) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onFirstFix(timeToFirstFix);
- }
- };
- foreach(operation);
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ listener.onFirstFix(timeToFirstFix);
+ }
+ );
}
public void onSvStatusChanged(
@@ -79,30 +72,23 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus
final float[] elevations,
final float[] azimuths,
final float[] carrierFreqs) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onSvStatusChanged(
- svCount,
- prnWithFlags,
- cn0s,
- elevations,
- azimuths,
- carrierFreqs);
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(TAG, packageName, "GNSS status");
+ return;
}
- };
- foreach(operation);
+ listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
+ carrierFreqs);
+ });
}
public void onNmeaReceived(final long timestamp, final String nmea) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGnssStatusListener listener) throws RemoteException {
- listener.onNmeaReceived(timestamp, nmea);
+ foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(TAG, packageName, "NMEA");
+ return;
}
- };
- foreach(operation);
+ listener.onNmeaReceived(timestamp, nmea);
+ });
}
-
- private interface Operation extends ListenerOperation<IGnssStatusListener> {}
}
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
deleted file mode 100644
index 678596445fe3..000000000000
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.location;
-
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.os.WorkSource;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Location Manager's interface for location providers.
- * @hide
- */
-public abstract class LocationProviderInterface {
-
- /** Get name. */
- public abstract String getName();
-
- /** Enable. */
- public abstract void enable();
-
- /** Disable. */
- public abstract void disable();
-
- /** Is enabled. */
- public abstract boolean isEnabled();
-
- /** Set request. */
- public abstract void setRequest(ProviderRequest request, WorkSource source);
-
- /** dump. */
- public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
- /** Get properties. */
- public abstract ProviderProperties getProperties();
-
- /**
- * Get status.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public int getStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- /**
- * Get status update time.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public long getStatusUpdateTime() {
- return 0;
- }
-
- /** Send extra command. */
- public abstract boolean sendExtraCommand(String command, Bundle extras);
-}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index b40841467946..dfcef70c8248 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,6 +18,7 @@ package com.android.server.location;
import android.annotation.Nullable;
import android.content.Context;
+import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
@@ -27,6 +28,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.os.BackgroundThread;
@@ -41,21 +43,35 @@ import java.io.PrintWriter;
/**
* Proxy for ILocationProvider implementations.
*/
-public class LocationProviderProxy extends LocationProviderInterface {
+public class LocationProviderProxy extends AbstractLocationProvider {
+
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
- private final ServiceWatcher mServiceWatcher;
-
- private final String mName;
-
// used to ensure that updates to mRequest and mWorkSource are atomic
private final Object mRequestLock = new Object();
+ private final ServiceWatcher mServiceWatcher;
- private volatile boolean mEnabled = false;
- @Nullable
- private volatile ProviderProperties mProperties;
+ private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
+ // executed on binder thread
+ @Override
+ public void onSetEnabled(boolean enabled) {
+ LocationProviderProxy.this.setEnabled(enabled);
+ }
+
+ // executed on binder thread
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+ LocationProviderProxy.this.setProperties(properties);
+ }
+
+ // executed on binder thread
+ @Override
+ public void onReportLocation(Location location) {
+ LocationProviderProxy.this.reportLocation(location);
+ }
+ };
@GuardedBy("mRequestLock")
@Nullable
@@ -69,10 +85,10 @@ public class LocationProviderProxy extends LocationProviderInterface {
*/
@Nullable
public static LocationProviderProxy createAndBind(
- Context context, String name, String action,
+ Context context, LocationProviderManager locationProviderManager, String action,
int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
- LocationProviderProxy proxy = new LocationProviderProxy(context, name,
+ LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
action, overlaySwitchResId, defaultServicePackageNameResId,
initialPackageNamesResId);
if (proxy.bind()) {
@@ -82,21 +98,27 @@ public class LocationProviderProxy extends LocationProviderInterface {
}
}
- private LocationProviderProxy(Context context, String name,
+ private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
String action, int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
+ super(locationProviderManager, false);
mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
defaultServicePackageNameResId, initialPackageNamesResId,
BackgroundThread.getHandler()) {
+
@Override
protected void onBind() {
runOnBinder(LocationProviderProxy.this::initializeService);
}
+
+ @Override
+ protected void onUnbind() {
+ setEnabled(false);
+ setProperties(null);
+ }
};
- mName = name;
- mProperties = null;
mRequest = null;
mWorkSource = new WorkSource();
}
@@ -107,84 +129,27 @@ public class LocationProviderProxy extends LocationProviderInterface {
private void initializeService(IBinder binder) {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- if (D) Log.d(TAG, "applying state to connected service");
-
- ProviderProperties[] properties = new ProviderProperties[1];
- ProviderRequest request;
- WorkSource source;
- synchronized (mRequestLock) {
- request = mRequest;
- source = mWorkSource;
- }
+ if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
try {
- // load properties from provider
- properties[0] = service.getProperties();
- if (properties[0] == null) {
- Log.e(TAG, mServiceWatcher.getCurrentPackageName()
- + " has invalid location provider properties");
- }
+ service.setLocationProviderManager(mManager);
- // apply current state to new service
- if (mEnabled) {
- service.enable();
- if (request != null) {
- service.setRequest(request, source);
+ synchronized (mRequestLock) {
+ if (mRequest != null) {
+ service.setRequest(mRequest, mWorkSource);
}
}
} catch (RemoteException e) {
Log.w(TAG, e);
}
-
- mProperties = properties[0];
}
+ @Nullable
public String getConnectedPackageName() {
return mServiceWatcher.getCurrentPackageName();
}
@Override
- public String getName() {
- return mName;
- }
-
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
- }
-
- @Override
- public void enable() {
- mEnabled = true;
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- try {
- service.enable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- });
- }
-
- @Override
- public void disable() {
- mEnabled = false;
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- try {
- service.disable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- });
- }
-
- @Override
- public boolean isEnabled() {
- return mEnabled;
- }
-
- @Override
public void setRequest(ProviderRequest request, WorkSource source) {
synchronized (mRequestLock) {
mRequest = request;
@@ -202,60 +167,53 @@ public class LocationProviderProxy extends LocationProviderInterface {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.append("REMOTE SERVICE");
- pw.append(" name=").append(mName);
- pw.append(" pkg=").append(mServiceWatcher.getCurrentPackageName());
- pw.append(" version=").append(Integer.toString(mServiceWatcher.getCurrentPackageVersion()));
- pw.append('\n');
+ pw.println(" service=" + mServiceWatcher);
mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- TransferPipe.dumpAsync(service.asBinder(), fd, args);
+ TransferPipe.dumpAsync(binder, fd, args);
} catch (IOException | RemoteException e) {
- pw.println("Failed to dump location provider: " + e);
+ pw.println(" failed to dump location provider: " + e);
}
});
}
@Override
public int getStatus(Bundle extras) {
- int[] result = new int[]{LocationProvider.TEMPORARILY_UNAVAILABLE};
+ int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.getStatus(extras);
+ status[0] = service.getStatus(extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
+ return status[0];
}
@Override
public long getStatusUpdateTime() {
- long[] result = new long[]{0L};
+ long[] updateTime = new long[] {0L};
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.getStatusUpdateTime();
+ updateTime[0] = service.getStatusUpdateTime();
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
+ return updateTime[0];
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- boolean[] result = new boolean[]{false};
+ public void sendExtraCommand(String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
- result[0] = service.sendExtraCommand(command, extras);
+ service.sendExtraCommand(command, extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
- return result[0];
}
}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 145aee3a9e6a..bfbebf74e93d 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -16,14 +16,11 @@
package com.android.server.location;
-import android.location.ILocationManager;
+import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationProvider;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.WorkSource;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -36,61 +33,55 @@ import java.io.PrintWriter;
*
* {@hide}
*/
-public class MockProvider extends LocationProviderInterface {
- private final String mName;
- private final ProviderProperties mProperties;
- private final ILocationManager mLocationManager;
+public class MockProvider extends AbstractLocationProvider {
- private final Location mLocation;
-
- private boolean mHasLocation;
private boolean mEnabled;
-
-
+ @Nullable private Location mLocation;
private int mStatus;
private long mStatusUpdateTime;
private Bundle mExtras;
- private static final String TAG = "MockProvider";
-
- public MockProvider(String name, ILocationManager locationManager,
- ProviderProperties properties) {
- if (properties == null) throw new NullPointerException("properties is null");
-
- mName = name;
- mLocationManager = locationManager;
- mProperties = properties;
- mLocation = new Location(name);
+ public MockProvider(
+ LocationProviderManager locationProviderManager, ProviderProperties properties) {
+ super(locationProviderManager, true);
+ mEnabled = true;
+ mLocation = null;
mStatus = LocationProvider.AVAILABLE;
- mStatusUpdateTime = 0L;
+ mStatusUpdateTime = 0;
mExtras = null;
+
+ setProperties(properties);
}
- @Override
- public String getName() {
- return mName;
+ /** Sets the enabled state of this mock provider. */
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ super.setEnabled(enabled);
}
- @Override
- public ProviderProperties getProperties() {
- return mProperties;
+ /** Sets the location to report for this mock provider. */
+ public void setLocation(Location l) {
+ mLocation = new Location(l);
+ if (mEnabled) {
+ reportLocation(l);
+ }
}
- @Override
- public void disable() {
- mEnabled = false;
+ /** Sets the status for this mock provider. */
+ public void setStatus(int status, Bundle extras, long updateTime) {
+ mStatus = status;
+ mStatusUpdateTime = updateTime;
+ mExtras = extras;
}
@Override
- public void enable() {
- mEnabled = true;
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(" last location=" + mLocation);
}
@Override
- public boolean isEnabled() {
- return mEnabled;
- }
+ public void setRequest(ProviderRequest request, WorkSource source) {}
@Override
public int getStatus(Bundle extras) {
@@ -107,50 +98,6 @@ public class MockProvider extends LocationProviderInterface {
return mStatusUpdateTime;
}
- public void setLocation(Location l) {
- mLocation.set(l);
- mHasLocation = true;
- if (mEnabled) {
- try {
- mLocationManager.reportLocation(mLocation, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
- }
- }
-
- public void clearLocation() {
- mHasLocation = false;
- }
-
- /**
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public void setStatus(int status, Bundle extras, long updateTime) {
- mStatus = status;
- mStatusUpdateTime = updateTime;
- mExtras = extras;
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- dump(pw, "");
- }
-
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + mName);
- pw.println(prefix + "mHasLocation=" + mHasLocation);
- pw.println(prefix + "mLocation:");
- mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- pw.println(prefix + "mExtras=" + mExtras);
- }
-
@Override
- public void setRequest(ProviderRequest request, WorkSource source) { }
-
- @Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
+ public void sendExtraCommand(String command, Bundle extras) {}
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 99c92149fa1b..70d64b02e4b4 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -17,13 +17,9 @@
package com.android.server.location;
import android.location.Criteria;
-import android.location.ILocationManager;
import android.location.Location;
-import android.location.LocationManager;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.WorkSource;
-import android.util.Log;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -38,41 +34,20 @@ import java.io.PrintWriter;
*
* {@hide}
*/
-public class PassiveProvider extends LocationProviderInterface {
- private static final String TAG = "PassiveProvider";
+public class PassiveProvider extends AbstractLocationProvider {
private static final ProviderProperties PROPERTIES = new ProviderProperties(
false, false, false, false, false, false, false,
Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
- private final ILocationManager mLocationManager;
private boolean mReportLocation;
- public PassiveProvider(ILocationManager locationManager) {
- mLocationManager = locationManager;
- }
-
- @Override
- public String getName() {
- return LocationManager.PASSIVE_PROVIDER;
- }
-
- @Override
- public ProviderProperties getProperties() {
- return PROPERTIES;
- }
+ public PassiveProvider(LocationProviderManager locationProviderManager) {
+ super(locationProviderManager, true);
- @Override
- public boolean isEnabled() {
- return true;
- }
+ mReportLocation = false;
- @Override
- public void enable() {
- }
-
- @Override
- public void disable() {
+ setProperties(PROPERTIES);
}
@Override
@@ -82,22 +57,15 @@ public class PassiveProvider extends LocationProviderInterface {
public void updateLocation(Location location) {
if (mReportLocation) {
- try {
- // pass the location back to the location manager
- mLocationManager.reportLocation(location, true);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
- }
+ reportLocation(location);
}
}
@Override
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
+ public void sendExtraCommand(String command, Bundle extras) {}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mReportLocation=" + mReportLocation);
+ pw.println(" report location=" + mReportLocation);
}
}
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index fcdb9d1a87ca..37d43fc1da69 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -17,6 +17,8 @@
package com.android.server.location;
import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
@@ -46,6 +48,9 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
+ protected final Context mContext;
+ protected final AppOpsManager mAppOps;
+
private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
private boolean mHasIsSupported;
@@ -53,10 +58,12 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
private int mLastReportedResult = RESULT_UNKNOWN;
- protected RemoteListenerHelper(Handler handler, String name) {
+ protected RemoteListenerHelper(Context context, Handler handler, String name) {
Preconditions.checkNotNull(name);
mHandler = handler;
mTag = name;
+ mContext = context;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
// read-only access for a dump() thread assured via volatile
@@ -64,10 +71,10 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
return mIsRegistered;
}
- public boolean addListener(@NonNull TListener listener) {
+ public boolean addListener(@NonNull TListener listener, int uid, String packageName) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
IBinder binder = listener.asBinder();
- LinkedListener deathListener = new LinkedListener(listener);
+ LinkedListener deathListener = new LinkedListener(listener, uid, packageName);
synchronized (mListenerMap) {
if (mListenerMap.containsKey(binder)) {
// listener already added
@@ -102,7 +109,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
// asynchronously in the future
return true;
}
- post(listener, getHandlerOperation(result));
+ post(deathListener, getHandlerOperation(result));
}
return true;
}
@@ -130,7 +137,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
protected interface ListenerOperation<TListener extends IInterface> {
- void execute(TListener listener) throws RemoteException;
+ void execute(TListener listener, int uid, String packageName) throws RemoteException;
}
protected void foreach(ListenerOperation<TListener> operation) {
@@ -170,15 +177,28 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
}
}
+ protected boolean hasPermission(int uid, String packageName) {
+ return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ protected void logPermissionDisabledEventNotReported(String tag, String packageName,
+ String event) {
+ if (Log.isLoggable(tag, Log.DEBUG)) {
+ Log.d(tag, "Location permission disabled. Skipping " + event + " reporting for app: "
+ + packageName);
+ }
+ }
+
private void foreachUnsafe(ListenerOperation<TListener> operation) {
for (LinkedListener linkedListener : mListenerMap.values()) {
- post(linkedListener.getUnderlyingListener(), operation);
+ post(linkedListener, operation);
}
}
- private void post(TListener listener, ListenerOperation<TListener> operation) {
+ private void post(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
if (operation != null) {
- mHandler.post(new HandlerRunnable(listener, operation));
+ mHandler.post(new HandlerRunnable(linkedListener, operation));
}
}
@@ -193,13 +213,9 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
}
if (!mIsRegistered) {
// post back a failure
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mListenerMap) {
- ListenerOperation<TListener> operation = getHandlerOperation(registrationState);
- foreachUnsafe(operation);
- }
+ mHandler.post(() -> {
+ synchronized (mListenerMap) {
+ foreachUnsafe(getHandlerOperation(registrationState));
}
});
}
@@ -208,16 +224,14 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
}
private void tryUnregister() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (!mIsRegistered) {
- return;
+ mHandler.post(() -> {
+ if (!mIsRegistered) {
+ return;
+ }
+ unregisterFromService();
+ mIsRegistered = false;
}
- unregisterFromService();
- mIsRegistered = false;
- }
- });
+ );
}
private int calculateCurrentResultUnsafe() {
@@ -240,14 +254,13 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
private class LinkedListener implements IBinder.DeathRecipient {
private final TListener mListener;
+ private final int mUid;
+ private final String mPackageName;
- public LinkedListener(@NonNull TListener listener) {
+ LinkedListener(@NonNull TListener listener, int uid, String packageName) {
mListener = listener;
- }
-
- @NonNull
- public TListener getUnderlyingListener() {
- return mListener;
+ mUid = uid;
+ mPackageName = packageName;
}
@Override
@@ -258,18 +271,19 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
}
private class HandlerRunnable implements Runnable {
- private final TListener mListener;
+ private final LinkedListener mLinkedListener;
private final ListenerOperation<TListener> mOperation;
- public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
- mListener = listener;
+ HandlerRunnable(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
+ mLinkedListener = linkedListener;
mOperation = operation;
}
@Override
public void run() {
try {
- mOperation.execute(mListener);
+ mOperation.execute(mLinkedListener.mListener, mLinkedListener.mUid,
+ mLinkedListener.mPackageName);
} catch (RemoteException e) {
Log.v(mTag, "Error in monitored listener.", e);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index 6e08949b634e..26e82704b357 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -16,13 +16,17 @@
package com.android.server.locksettings.recoverablekeystore.certificate;
-import static javax.xml.xpath.XPathConstants.NODESET;
-
import android.annotation.IntDef;
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -40,7 +44,6 @@ import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
-import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
@@ -58,15 +61,6 @@ import java.util.Set;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
/** Utility functions related to parsing and validating public-key certificates. */
public final class CertUtils {
@@ -167,50 +161,63 @@ public final class CertUtils {
static List<String> getXmlNodeContents(@MustExist int mustExist, Element rootNode,
String... nodeTags)
throws CertParsingException {
- String expression = String.join("/", nodeTags);
-
- XPath xPath = XPathFactory.newInstance().newXPath();
- NodeList nodeList;
- try {
- nodeList = (NodeList) xPath.compile(expression).evaluate(rootNode, NODESET);
- } catch (XPathExpressionException e) {
- throw new CertParsingException(e);
+ if (nodeTags.length == 0) {
+ throw new CertParsingException("The tag list must not be empty");
}
- switch (mustExist) {
- case MUST_EXIST_UNENFORCED:
- break;
-
- case MUST_EXIST_EXACTLY_ONE:
- if (nodeList.getLength() != 1) {
- throw new CertParsingException(
- "The XML file must contain exactly one node with the path "
- + expression);
- }
- break;
-
- case MUST_EXIST_AT_LEAST_ONE:
- if (nodeList.getLength() == 0) {
- throw new CertParsingException(
- "The XML file must contain at least one node with the path "
- + expression);
- }
- break;
-
- default:
- throw new UnsupportedOperationException(
- "This value of MustExist is not supported: " + mustExist);
+ // Go down through all the intermediate node tags (except the last tag for the leaf nodes).
+ // Note that this implementation requires that at most one path exists for the given
+ // intermediate node tags.
+ Element parent = rootNode;
+ for (int i = 0; i < nodeTags.length - 1; i++) {
+ String tag = nodeTags[i];
+ List<Element> children = getXmlDirectChildren(parent, tag);
+ if ((children.size() == 0 && mustExist != MUST_EXIST_UNENFORCED)
+ || children.size() > 1) {
+ throw new CertParsingException(
+ "The XML file must contain exactly one path with the tag " + tag);
+ }
+ if (children.size() == 0) {
+ return new ArrayList<>();
+ }
+ parent = children.get(0);
}
+ // Then collect the contents of the leaf nodes.
+ List<Element> leafs = getXmlDirectChildren(parent, nodeTags[nodeTags.length - 1]);
+ if (mustExist == MUST_EXIST_EXACTLY_ONE && leafs.size() != 1) {
+ throw new CertParsingException(
+ "The XML file must contain exactly one node with the path "
+ + String.join("/", nodeTags));
+ }
+ if (mustExist == MUST_EXIST_AT_LEAST_ONE && leafs.size() == 0) {
+ throw new CertParsingException(
+ "The XML file must contain at least one node with the path "
+ + String.join("/", nodeTags));
+ }
List<String> result = new ArrayList<>();
- for (int i = 0; i < nodeList.getLength(); i++) {
- Node node = nodeList.item(i);
+ for (Element leaf : leafs) {
// Remove whitespaces and newlines.
- result.add(node.getTextContent().replaceAll("\\s", ""));
+ result.add(leaf.getTextContent().replaceAll("\\s", ""));
}
return result;
}
+ /** Get the direct child nodes with a given tag. */
+ private static List<Element> getXmlDirectChildren(Element parent, String tag) {
+ // Cannot use Element.getElementsByTagName because it will return all descendant elements
+ // with the tag name, i.e. not only the direct child nodes.
+ List<Element> children = new ArrayList<>();
+ NodeList childNodes = parent.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node node = childNodes.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(tag)) {
+ children.add((Element) node);
+ }
+ }
+ return children;
+ }
+
/**
* Decodes a base64-encoded string.
*
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f19ecf3ebd22..fc364c01aa4f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -207,6 +207,7 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
+import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.lights.Light;
@@ -265,7 +266,7 @@ public class NotificationManagerService extends SystemService {
// message codes
static final int MESSAGE_DURATION_REACHED = 2;
- static final int MESSAGE_SAVE_POLICY_FILE = 3;
+ // 3: removed to a different handler
static final int MESSAGE_SEND_RANKING_UPDATE = 4;
static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
@@ -573,7 +574,7 @@ public class NotificationManagerService extends SystemService {
mListeners.migrateToXml();
mAssistants.migrateToXml();
mConditionProviders.migrateToXml();
- savePolicyFile();
+ handleSavePolicyFile();
}
mAssistants.ensureAssistant();
@@ -603,34 +604,29 @@ public class NotificationManagerService extends SystemService {
}
}
- /**
- * Saves notification policy
- */
- public void savePolicyFile() {
- mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
- mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
- }
-
- private void handleSavePolicyFile() {
- if (DBG) Slog.d(TAG, "handleSavePolicyFile");
- synchronized (mPolicyFile) {
- final FileOutputStream stream;
- try {
- stream = mPolicyFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file", e);
- return;
- }
+ @VisibleForTesting
+ protected void handleSavePolicyFile() {
+ IoThread.getHandler().post(() -> {
+ if (DBG) Slog.d(TAG, "handleSavePolicyFile");
+ synchronized (mPolicyFile) {
+ final FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
+ }
- try {
- writePolicyXml(stream, false /*forBackup*/);
- mPolicyFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file, restoring backup", e);
- mPolicyFile.failWrite(stream);
+ try {
+ writePolicyXml(stream, false /*forBackup*/);
+ mPolicyFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+ mPolicyFile.failWrite(stream);
+ }
}
- }
- BackupManager.dataChanged(getContext().getPackageName());
+ BackupManager.dataChanged(getContext().getPackageName());
+ });
}
private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
@@ -1138,8 +1134,9 @@ public class NotificationManagerService extends SystemService {
}
}
+
mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
- savePolicyFile();
+ handleSavePolicyFile();
}
}
};
@@ -1206,7 +1203,7 @@ public class NotificationManagerService extends SystemService {
mListeners.onUserRemoved(userId);
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
- savePolicyFile();
+ handleSavePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
@@ -1462,7 +1459,7 @@ public class NotificationManagerService extends SystemService {
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -1764,7 +1761,7 @@ public class NotificationManagerService extends SystemService {
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
@@ -2232,7 +2229,7 @@ public class NotificationManagerService extends SystemService {
Slog.w(TAG, "Can't notify app about app block change", e);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
/**
@@ -2289,7 +2286,7 @@ public class NotificationManagerService extends SystemService {
public void setShowBadge(String pkg, int uid, boolean showBadge) {
checkCallerIsSystem();
mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2305,7 +2302,7 @@ public class NotificationManagerService extends SystemService {
if (info != null) {
mPreferencesHelper.setNotificationDelegate(
callingPkg, callingUid, delegate, info.uid);
- savePolicyFile();
+ handleSavePolicyFile();
}
} catch (RemoteException e) {
// :(
@@ -2316,7 +2313,7 @@ public class NotificationManagerService extends SystemService {
public void revokeNotificationDelegate(String callingPkg) {
checkCallerIsSameApp(callingPkg);
mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2351,7 +2348,7 @@ public class NotificationManagerService extends SystemService {
NotificationChannelGroup group) throws RemoteException {
enforceSystemOrSystemUI("Caller not system or systemui");
createNotificationChannelGroup(pkg, uid, group, false, false);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2364,7 +2361,7 @@ public class NotificationManagerService extends SystemService {
final NotificationChannelGroup group = groups.get(i);
createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
private void createNotificationChannelsImpl(String pkg, int uid,
@@ -2382,7 +2379,7 @@ public class NotificationManagerService extends SystemService {
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2427,7 +2424,7 @@ public class NotificationManagerService extends SystemService {
UserHandle.getUserHandleForUid(callingUid),
mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2469,7 +2466,7 @@ public class NotificationManagerService extends SystemService {
mListeners.notifyNotificationChannelGroupChanged(
pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -2602,7 +2599,7 @@ public class NotificationManagerService extends SystemService {
true, UserHandle.getCallingUserId(), packages, uids);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
@@ -3390,7 +3387,7 @@ public class NotificationManagerService extends SystemService {
final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
try {
readPolicyXml(bais, true /*forRestore*/);
- savePolicyFile();
+ handleSavePolicyFile();
} catch (NumberFormatException | XmlPullParserException | IOException e) {
Slog.w(TAG, "applyRestore: error reading payload", e);
}
@@ -3431,7 +3428,7 @@ public class NotificationManagerService extends SystemService {
.setPackage(pkg)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3575,7 +3572,7 @@ public class NotificationManagerService extends SystemService {
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3601,7 +3598,7 @@ public class NotificationManagerService extends SystemService {
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3624,12 +3621,19 @@ public class NotificationManagerService extends SystemService {
&& mAssistants.isSameUser(token, r.getUserId())) {
applyAdjustment(r, adjustment);
r.applyAdjustments();
+ // importance is checked at the beginning of the
+ // PostNotificationRunnable, before the signal extractors are run, so
+ // calculate the final importance here
+ r.calculateImportance();
foundEnqueued = true;
break;
}
}
if (!foundEnqueued) {
// adjustment arrived too late to apply to enqueued; apply to posted
+ // However, since the notification is now posted and may have alerted,
+ // ignore any importance related adjustments
+ adjustment.getSignals().remove(Adjustment.KEY_IMPORTANCE);
applyAdjustmentFromAssistant(token, adjustment);
}
}
@@ -3659,8 +3663,12 @@ public class NotificationManagerService extends SystemService {
NotificationRecord r = mNotificationsByKey.get(adjustment.getKey());
if (r != null && mAssistants.isSameUser(token, r.getUserId())) {
applyAdjustment(r, adjustment);
- r.applyImportanceFromAdjustments();
- if (r.getImportance() == IMPORTANCE_NONE) {
+ // If the assistant has blocked the notification, cancel it
+ // This will trigger a sort, so we don't have to explicitly ask for
+ // one here.
+ if (adjustment.getSignals().containsKey(Adjustment.KEY_IMPORTANCE)
+ && adjustment.getSignals().getInt(Adjustment.KEY_IMPORTANCE)
+ == IMPORTANCE_NONE) {
cancelNotificationsFromListener(token, new String[]{r.getKey()});
} else {
needsSort = true;
@@ -3684,7 +3692,7 @@ public class NotificationManagerService extends SystemService {
verifyPrivilegedListener(token, user, false);
createNotificationChannelGroup(
pkg, getUidForPackageAndUser(pkg, user), group, false, true);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -3733,7 +3741,7 @@ public class NotificationManagerService extends SystemService {
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -4492,7 +4500,7 @@ public class NotificationManagerService extends SystemService {
Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
mSnoozeHelper.update(userId, r);
- savePolicyFile();
+ handleSavePolicyFile();
return false;
}
@@ -4623,7 +4631,7 @@ public class NotificationManagerService extends SystemService {
mSnoozeHelper.snooze(r, mDuration);
}
r.recordSnoozed();
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -4701,7 +4709,7 @@ public class NotificationManagerService extends SystemService {
if (mReason != REASON_SNOOZED) {
final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId);
if (wasSnoozed) {
- savePolicyFile();
+ handleSavePolicyFile();
}
}
}
@@ -4761,7 +4769,6 @@ public class NotificationManagerService extends SystemService {
enqueueStatus);
}
- mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
mAssistants.onNotificationEnqueuedLocked(r);
@@ -4845,7 +4852,7 @@ public class NotificationManagerService extends SystemService {
| Notification.FLAG_NO_CLEAR;
}
- applyZenModeLocked(r);
+ mRankingHelper.extractSignals(r);
mRankingHelper.sort(mNotificationList);
if (!r.isHidden()) {
@@ -5630,6 +5637,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
+ int[] importancesBefore = new int[N];
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -5643,6 +5651,7 @@ public class NotificationManagerService extends SystemService {
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
smartRepliesBefore.add(r.getSmartReplies());
+ importancesBefore[i] = r.getImportance();
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -5660,7 +5669,8 @@ public class NotificationManagerService extends SystemService {
r.getSuppressedVisualEffects())
|| !Objects.equals(systemSmartActionsBefore.get(i),
r.getSystemGeneratedSmartActions())
- || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
+ || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())
+ || importancesBefore[i] != r.getImportance()) {
mHandler.scheduleSendRankingUpdate();
return;
}
@@ -5750,9 +5760,6 @@ public class NotificationManagerService extends SystemService {
case MESSAGE_FINISH_TOKEN_TIMEOUT:
handleKillTokenTimeout((ToastRecord) msg.obj);
break;
- case MESSAGE_SAVE_POLICY_FILE:
- handleSavePolicyFile();
- break;
case MESSAGE_SEND_RANKING_UPDATE:
handleSendRankingUpdate();
break;
@@ -6270,7 +6277,7 @@ public class NotificationManagerService extends SystemService {
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
mSnoozeHelper.repost(key);
- savePolicyFile();
+ handleSavePolicyFile();
}
@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 1a9257cf17fc..50810ccc8cbc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,10 +21,8 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -609,6 +607,17 @@ public final class NotificationRecord {
mIsAppImportanceLocked, this.sbn.getNotification());
}
+ public boolean hasAdjustment(String key) {
+ synchronized (mAdjustments) {
+ for (Adjustment adjustment : mAdjustments) {
+ if (adjustment.getSignals().containsKey(key)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void addAdjustment(Adjustment adjustment) {
synchronized (mAdjustments) {
mAdjustments.add(adjustment);
@@ -669,18 +678,6 @@ public final class NotificationRecord {
.addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
getSmartReplies().size()));
}
- }
- applyImportanceFromAdjustments();
- }
- }
-
- /**
- * Update importance from the adjustment.
- */
- public void applyImportanceFromAdjustments() {
- synchronized (mAdjustments) {
- for (Adjustment adjustment : mAdjustments) {
- Bundle signals = adjustment.getSignals();
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
@@ -752,6 +749,8 @@ public final class NotificationRecord {
*/
public void setSystemImportance(int importance) {
mSystemImportance = importance;
+ // System importance is only changed in enqueue, so it's ok for us to calculate the
+ // importance directly instead of waiting for signal extractor.
calculateImportance();
}
@@ -762,7 +761,16 @@ public final class NotificationRecord {
*/
public void setAssistantImportance(int importance) {
mAssistantImportance = importance;
- calculateImportance();
+ // Unlike the system importance, the assistant importance can change on posted
+ // notifications, so don't calculateImportance() here, but wait for the signal extractors.
+ }
+
+ /**
+ * Returns the importance set by the assistant, or IMPORTANCE_UNSPECIFIED if the assistant
+ * hasn't set it.
+ */
+ public int getAssistantImportance() {
+ return mAssistantImportance;
}
/**
@@ -774,7 +782,8 @@ public final class NotificationRecord {
if (getChannel().hasUserSetImportance()) {
mImportanceExplanation = "user";
}
- if (!getChannel().hasUserSetImportance() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+ if (!getChannel().hasUserSetImportance()
+ && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
mImportance = mAssistantImportance;
mImportanceExplanation = "asst";
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 65ccecdcafff..7ae2271adb19 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -19,6 +19,7 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
@@ -77,18 +78,20 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
IApplicationThread caller,
String callingPackage,
ComponentName component,
- UserHandle user) throws RemoteException {
+ @UserIdInt int userId,
+ boolean launchMainActivity) throws RemoteException {
Preconditions.checkNotNull(callingPackage);
Preconditions.checkNotNull(component);
- Preconditions.checkNotNull(user);
verifyCallingPackage(callingPackage);
+ final int callerUserId = mInjector.getCallingUserId();
+ final int callingUid = mInjector.getCallingUid();
+
List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
- callingPackage, mInjector.getCallingUserId());
- if (!allowedTargetUsers.contains(user)) {
- throw new SecurityException(
- callingPackage + " cannot access unrelated user " + user.getIdentifier());
+ callingPackage, callerUserId);
+ if (!allowedTargetUsers.contains(UserHandle.of(userId))) {
+ throw new SecurityException(callingPackage + " cannot access unrelated user " + userId);
}
// Verify that caller package is starting activity in its own package.
@@ -98,25 +101,43 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
+ component.getPackageName());
}
- final int callingUid = mInjector.getCallingUid();
-
- // Verify that target activity does handle the intent with ACTION_MAIN and
- // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
- final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
- launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- // Only package name is set here, as opposed to component name, because intent action and
- // category are ignored if component name is present while we are resolving intent.
- launchIntent.setPackage(component.getPackageName());
- verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user);
+ // Verify that target activity does handle the intent correctly.
+ final Intent launchIntent = new Intent();
+ if (launchMainActivity) {
+ launchIntent.setAction(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ // Only package name is set here, as opposed to component name, because intent action
+ // and category are ignored if component name is present while we are resolving intent.
+ launchIntent.setPackage(component.getPackageName());
+ } else {
+ // If the main activity is not being launched and the users are different, the caller
+ // must have the required permission and the users must be in the same profile group
+ // in order to launch any of its own activities.
+ if (callerUserId != userId) {
+ final int permissionFlag = ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
+ -1, true);
+ if (permissionFlag != PackageManager.PERMISSION_GRANTED
+ || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+ throw new SecurityException("Attempt to launch activity without required "
+ + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
+ + " or target user is not in the same profile group.");
+ }
+ }
+ launchIntent.setComponent(component);
+ }
+ verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, userId);
launchIntent.setPackage(null);
launchIntent.setComponent(component);
mInjector.getActivityTaskManagerInternal().startActivityAsUser(
caller, callingPackage, launchIntent,
- ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
- user.getIdentifier());
+ launchMainActivity
+ ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
+ : null,
+ userId);
}
private List<UserHandle> getTargetUserProfilesUnchecked(
@@ -163,7 +184,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
* activity is exported.
*/
private void verifyActivityCanHandleIntentAndExported(
- Intent launchIntent, ComponentName component, int callingUid, UserHandle user) {
+ Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) {
final long ident = mInjector.clearCallingIdentity();
try {
final List<ResolveInfo> apps =
@@ -171,7 +192,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
launchIntent,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
callingUid,
- user.getIdentifier());
+ userId);
final int size = apps.size();
for (int i = 0; i < size; ++i) {
final ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b12d4b6b504e..edab94c963f1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -86,11 +86,6 @@ import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageParser.isApkFile;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
@@ -242,6 +237,7 @@ import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.provider.MediaStore;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
@@ -1301,6 +1297,8 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
final @Nullable String mSystemTextClassifierPackage;
+ final @Nullable String mWellbeingPackage;
+ final @Nullable String mDocumenterPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
@@ -2093,28 +2091,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @GuardedBy("mPackages")
- private void setupBuiltinSharedLibraryDependenciesLocked() {
- // Builtin libraries don't have versions.
- long version = SharedLibraryInfo.VERSION_UNDEFINED;
-
- SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ANDROID_HIDL_MANAGER, version);
- if (libraryInfo != null) {
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_HIDL_BASE, version));
- }
-
- libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_RUNNER, version);
- if (libraryInfo != null) {
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version));
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
- }
-
- libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version);
- if (libraryInfo != null) {
- libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
- }
- }
-
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
@@ -2222,17 +2198,32 @@ public class PackageManagerService extends IPackageManager.Stub
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this);
- ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
+ ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
+ = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
- String path = libConfig.valueAt(i);
- addSharedLibraryLPw(path, null, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
- SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
+ SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
+ addSharedLibraryLPw(entry.filename, null, null, name,
+ SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
+ PLATFORM_PACKAGE_NAME, 0);
+ }
+
+ // Now that we have added all the libraries, iterate again to add dependency
+ // information IFF their dependencies are added.
+ long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
+ for (int i = 0; i < builtInLibCount; i++) {
+ String name = libConfig.keyAt(i);
+ SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
+ final int dependencyCount = entry.dependencies.length;
+ for (int j = 0; j < dependencyCount; j++) {
+ final SharedLibraryInfo dependency =
+ getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);
+ if (dependency != null) {
+ getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);
+ }
+ }
}
- // Builtin libraries cannot encode their dependency where they are
- // defined, so fix that now.
- setupBuiltinSharedLibraryDependenciesLocked();
SELinuxMMAC.readInstallPolicy();
@@ -2790,6 +2781,9 @@ public class PackageManagerService extends IPackageManager.Stub
mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
+ mWellbeingPackage = getWellbeingPackageName();
+ mDocumenterPackage = getDocumenterPackageName();
+
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLPw(null);
@@ -17933,7 +17927,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int removedUserId = (user != null) ? user.getIdentifier()
: UserHandle.USER_ALL;
- clearPackageStateForUserLIF(ps, removedUserId, outInfo);
+ clearPackageStateForUserLIF(ps, removedUserId, outInfo, flags);
markPackageUninstalledForUserLPw(ps, user);
scheduleWritePackageRestrictionsLocked(user);
return;
@@ -17963,7 +17957,7 @@ public class PackageManagerService extends IPackageManager.Stub
// we need to do is clear this user's data and save that
// it is uninstalled.
if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
- clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
+ clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo, flags);
scheduleWritePackageRestrictionsLocked(user);
return;
} else {
@@ -17979,7 +17973,7 @@ public class PackageManagerService extends IPackageManager.Stub
// we need to do is clear this user's data and save that
// it is uninstalled.
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
- clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
+ clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo, flags);
scheduleWritePackageRestrictionsLocked(user);
return;
}
@@ -18096,7 +18090,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
- PackageRemovedInfo outInfo) {
+ PackageRemovedInfo outInfo, int flags) {
final PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(ps.name);
@@ -18121,6 +18115,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);
}
+ // Also delete contributed media, when requested
+ if ((flags & PackageManager.DELETE_CONTRIBUTED_MEDIA) != 0) {
+ try {
+ MediaStore.deleteContributedMedia(mContext, ps.name, UserHandle.of(nextUserId));
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to delete contributed media for " + ps.name, e);
+ }
+ }
}
if (outInfo != null) {
@@ -18307,6 +18309,10 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
+ if (bp.isRemoved()) {
+ continue;
+ }
+
// If shared user we just reset the state to which only this app contributed.
if (ps.sharedUser != null) {
boolean used = false;
@@ -19563,6 +19569,27 @@ public class PackageManagerService extends IPackageManager.Stub
return mContext.getString(R.string.config_defaultTextClassifierPackage);
}
+ private @Nullable String getDocumenterPackageName() {
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DISABLED_COMPONENTS,
+ UserHandle.myUserId());
+ if (matches.size() == 1) {
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ Slog.e(TAG, "There should probably be exactly one documenter; found "
+ + matches.size() + ": matches=" + matches);
+ return null;
+ }
+ }
+
+ @Override
+ public String getWellbeingPackageName() {
+ return mContext.getString(R.string.config_defaultWellbeingPackage);
+ }
+
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
@@ -22719,6 +22746,10 @@ public class PackageManagerService extends IPackageManager.Stub
return mSystemTextClassifierPackage;
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return mRequiredPermissionControllerPackage;
+ case PackageManagerInternal.PACKAGE_WELLBEING:
+ return mWellbeingPackage;
+ case PackageManagerInternal.PACKAGE_DOCUMENTER:
+ return mDocumenterPackage;
}
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c18ca25d8c1f..d1d5818b8c46 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -31,6 +31,7 @@ import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyEventLogger;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -73,6 +74,7 @@ import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.util.AtomicFile;
import android.util.IntArray;
import android.util.Log;
@@ -100,6 +102,8 @@ import com.android.server.am.UserState;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -121,8 +125,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
-import libcore.io.IoUtils;
-
/**
* Service for {@link UserManager}.
*
@@ -173,6 +175,8 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TAG_ENTRY = "entry";
private static final String TAG_VALUE = "value";
private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
+ private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL =
+ "lastRequestQuietModeEnabledCall";
private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE_TYPE = "type";
private static final String ATTR_MULTIPLE = "m";
@@ -268,6 +272,16 @@ public class UserManagerService extends IUserManager.Stub {
/** Elapsed realtime since boot when the user was unlocked. */
long unlockRealtime;
+ private long mLastRequestQuietModeEnabledMillis;
+
+ void setLastRequestQuietModeEnabledMillis(long millis) {
+ mLastRequestQuietModeEnabledMillis = millis;
+ }
+
+ long getLastRequestQuietModeEnabledMillis() {
+ return mLastRequestQuietModeEnabledMillis;
+ }
+
void clearSeedAccountData() {
seedAccountName = null;
seedAccountType = null;
@@ -389,8 +403,8 @@ public class UserManagerService extends IUserManager.Stub {
final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
// Call setQuietModeEnabled on bg thread to avoid ANR
- BackgroundThread.getHandler()
- .post(() -> setQuietModeEnabled(userHandle, false, target));
+ BackgroundThread.getHandler().post(() ->
+ setQuietModeEnabled(userHandle, false, target, /* callingPackage */ null));
}
};
@@ -834,21 +848,24 @@ public class UserManagerService extends IUserManager.Stub {
ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), target != null);
final long identity = Binder.clearCallingIdentity();
try {
+ boolean result = false;
if (enableQuietMode) {
- setQuietModeEnabled(userHandle, true /* enableQuietMode */, target);
- return true;
+ setQuietModeEnabled(
+ userHandle, true /* enableQuietMode */, target, callingPackage);
+ result = true;
} else {
boolean needToShowConfirmCredential =
mLockPatternUtils.isSecure(userHandle)
&& !StorageManager.isUserKeyUnlocked(userHandle);
if (needToShowConfirmCredential) {
showConfirmCredentialToDisableQuietMode(userHandle, target);
- return false;
} else {
- setQuietModeEnabled(userHandle, false /* enableQuietMode */, target);
- return true;
+ setQuietModeEnabled(
+ userHandle, false /* enableQuietMode */, target, callingPackage);
+ result = true;
}
}
+ return result;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -894,8 +911,8 @@ public class UserManagerService extends IUserManager.Stub {
+ "default launcher nor has MANAGE_USERS/MODIFY_QUIET_MODE permission");
}
- private void setQuietModeEnabled(
- int userHandle, boolean enableQuietMode, IntentSender target) {
+ private void setQuietModeEnabled(int userHandle, boolean enableQuietMode,
+ IntentSender target, @Nullable String callingPackage) {
final UserInfo profile, parent;
final UserData profileUserData;
synchronized (mUsersLock) {
@@ -927,6 +944,7 @@ public class UserManagerService extends IUserManager.Stub {
ActivityManager.getService().startUserInBackgroundWithListener(
userHandle, callback);
}
+ logQuietModeEnabled(userHandle, enableQuietMode, callingPackage);
} catch (RemoteException e) {
// Should not happen, same process.
e.rethrowAsRuntimeException();
@@ -935,6 +953,28 @@ public class UserManagerService extends IUserManager.Stub {
enableQuietMode);
}
+ private void logQuietModeEnabled(int userHandle, boolean enableQuietMode,
+ @Nullable String callingPackage) {
+ UserData userData;
+ synchronized (mUsersLock) {
+ userData = getUserDataLU(userHandle);
+ }
+ if (userData == null) {
+ return;
+ }
+ final long now = System.currentTimeMillis();
+ final long period = (userData.getLastRequestQuietModeEnabledMillis() != 0L
+ ? now - userData.getLastRequestQuietModeEnabledMillis()
+ : now - userData.info.creationTime);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REQUEST_QUIET_MODE_ENABLED)
+ .setStrings(callingPackage)
+ .setBoolean(enableQuietMode)
+ .setTimePeriod(period)
+ .write();
+ userData.setLastRequestQuietModeEnabledMillis(now);
+ }
+
@Override
public boolean isQuietModeEnabled(int userHandle) {
synchronized (mPackagesLock) {
@@ -2314,6 +2354,12 @@ public class UserManagerService extends IUserManager.Stub {
serializer.endTag(null, TAG_SEED_ACCOUNT_OPTIONS);
}
+ if (userData.getLastRequestQuietModeEnabledMillis() != 0L) {
+ serializer.startTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL);
+ serializer.text(String.valueOf(userData.getLastRequestQuietModeEnabledMillis()));
+ serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL);
+ }
+
serializer.endTag(null, TAG_USER);
serializer.endDocument();
@@ -2408,6 +2454,7 @@ public class UserManagerService extends IUserManager.Stub {
String iconPath = null;
long creationTime = 0L;
long lastLoggedInTime = 0L;
+ long lastRequestQuietModeEnabledTimestamp = 0L;
String lastLoggedInFingerprint = null;
int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
int profileBadge = 0;
@@ -2494,6 +2541,11 @@ public class UserManagerService extends IUserManager.Stub {
} else if (TAG_SEED_ACCOUNT_OPTIONS.equals(tag)) {
seedAccountOptions = PersistableBundle.restoreFromXml(parser);
persistSeedData = true;
+ } else if (TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText());
+ }
}
}
}
@@ -2518,6 +2570,7 @@ public class UserManagerService extends IUserManager.Stub {
userData.seedAccountType = seedAccountType;
userData.persistSeedData = persistSeedData;
userData.seedAccountOptions = seedAccountOptions;
+ userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp);
synchronized (mRestrictionsLock) {
if (baseRestrictions != null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 3a74ab51e9c7..36b7269576b6 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -16,6 +16,11 @@
package com.android.server.pm.dex;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -26,9 +31,9 @@ import android.database.ContentObserver;
import android.os.Build;
import android.os.FileUtils;
import android.os.RemoteException;
-import android.os.storage.StorageManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.provider.Settings.Global;
import android.util.Log;
import android.util.Slog;
@@ -48,18 +53,14 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
-
/**
* This class keeps track of how dex files are used.
* Every time it gets a notification about a dex file being loaded it tracks
@@ -89,6 +90,12 @@ public class DexManager {
// encode and save the dex usage data.
private final PackageDexUsage mPackageDexUsage;
+ // PackageDynamicCodeLoading handles recording of dynamic code loading -
+ // which is similar to PackageDexUsage but records a different aspect of the data.
+ // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't
+ // record class loaders or ISAs.)
+ private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+
private final IPackageManager mPackageManager;
private final PackageDexOptimizer mPackageDexOptimizer;
private final Object mInstallLock;
@@ -126,14 +133,15 @@ public class DexManager {
public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo,
Installer installer, Object installLock, Listener listener) {
- mContext = context;
- mPackageCodeLocationsCache = new HashMap<>();
- mPackageDexUsage = new PackageDexUsage();
- mPackageManager = pms;
- mPackageDexOptimizer = pdo;
- mInstaller = installer;
- mInstallLock = installLock;
- mListener = listener;
+ mContext = context;
+ mPackageCodeLocationsCache = new HashMap<>();
+ mPackageDexUsage = new PackageDexUsage();
+ mPackageDynamicCodeLoading = new PackageDynamicCodeLoading();
+ mPackageManager = pms;
+ mPackageDexOptimizer = pdo;
+ mInstaller = installer;
+ mInstallLock = installLock;
+ mListener = listener;
}
public void systemReady() {
@@ -207,7 +215,6 @@ public class DexManager {
Slog.i(TAG, loadingAppInfo.packageName +
" uses unsupported class loader in " + classLoaderNames);
}
- return;
}
int dexPathIndex = 0;
@@ -236,15 +243,24 @@ public class DexManager {
continue;
}
- // Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
- // or UsedByOtherApps), record will return true and we trigger an async write
- // to disk to make sure we don't loose the data in case of a reboot.
+ if (mPackageDynamicCodeLoading.record(searchResult.mOwningPackageName, dexPath,
+ PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
+ loadingAppInfo.packageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+
+ if (classLoaderContexts != null) {
- String classLoaderContext = classLoaderContexts[dexPathIndex];
- if (mPackageDexUsage.record(searchResult.mOwningPackageName,
- dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
- loadingAppInfo.packageName, classLoaderContext)) {
- mPackageDexUsage.maybeWriteAsync();
+ // Record dex file usage. If the current usage is a new pattern (e.g. new
+ // secondary, or UsedByOtherApps), record will return true and we trigger an
+ // async write to disk to make sure we don't loose the data in case of a reboot.
+
+ String classLoaderContext = classLoaderContexts[dexPathIndex];
+ if (mPackageDexUsage.record(searchResult.mOwningPackageName,
+ dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+ loadingAppInfo.packageName, classLoaderContext)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
}
} else {
// If we can't find the owner of the dex we simply do not track it. The impact is
@@ -268,8 +284,8 @@ public class DexManager {
loadInternal(existingPackages);
} catch (Exception e) {
mPackageDexUsage.clear();
- Slog.w(TAG, "Exception while loading package dex usage. " +
- "Starting with a fresh state.", e);
+ mPackageDynamicCodeLoading.clear();
+ Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
}
}
@@ -311,15 +327,24 @@ public class DexManager {
* all usage information for the package will be removed.
*/
public void notifyPackageDataDestroyed(String packageName, int userId) {
- boolean updated = userId == UserHandle.USER_ALL
- ? mPackageDexUsage.removePackage(packageName)
- : mPackageDexUsage.removeUserPackage(packageName, userId);
// In case there was an update, write the package use info to disk async.
- // Note that we do the writing here and not in PackageDexUsage in order to be
+ // Note that we do the writing here and not in the lower level classes in order to be
// consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
// multiple updates in PackageDexUsage before writing it).
- if (updated) {
- mPackageDexUsage.maybeWriteAsync();
+ if (userId == UserHandle.USER_ALL) {
+ if (mPackageDexUsage.removePackage(packageName)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ if (mPackageDynamicCodeLoading.removePackage(packageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+ } else {
+ if (mPackageDexUsage.removeUserPackage(packageName, userId)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
}
}
@@ -388,8 +413,23 @@ public class DexManager {
}
}
- mPackageDexUsage.read();
- mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+ try {
+ mPackageDexUsage.read();
+ mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+ } catch (Exception e) {
+ mPackageDexUsage.clear();
+ Slog.w(TAG, "Exception while loading package dex usage. "
+ + "Starting with a fresh state.", e);
+ }
+
+ try {
+ mPackageDynamicCodeLoading.read();
+ mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+ } catch (Exception e) {
+ mPackageDynamicCodeLoading.clear();
+ Slog.w(TAG, "Exception while loading package dynamic code usage. "
+ + "Starting with a fresh state.", e);
+ }
}
/**
@@ -415,10 +455,16 @@ public class DexManager {
* TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
* to access the package use.
*/
+ @VisibleForTesting
/*package*/ boolean hasInfoOnPackage(String packageName) {
return mPackageDexUsage.getPackageUseInfo(packageName) != null;
}
+ @VisibleForTesting
+ /*package*/ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+ return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
+ }
+
/**
* Perform dexopt on with the given {@code options} on the secondary dex files.
* @return true if all secondary dex files were processed successfully (compiled or skipped
@@ -652,7 +698,7 @@ public class DexManager {
// to load dex files through it.
try {
String dexPathReal = PackageManagerServiceUtils.realpath(new File(dexPath));
- if (dexPathReal != dexPath) {
+ if (!dexPath.equals(dexPathReal)) {
Slog.d(TAG, "Dex loaded with symlink. dexPath=" +
dexPath + " dexPathReal=" + dexPathReal);
}
@@ -675,6 +721,7 @@ public class DexManager {
*/
public void writePackageDexUsageNow() {
mPackageDexUsage.writeNow();
+ mPackageDynamicCodeLoading.writeNow();
}
private void registerSettingObserver() {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
new file mode 100644
index 000000000000..f74aa1d69bc8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastPrintWriter;
+import com.android.server.pm.AbstractStatsBase;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Stats file which stores information about secondary code files that are dynamically loaded.
+ */
+class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
+ // Type code to indicate a secondary file containing DEX code. (The char value is how it
+ // is represented in the text file format.)
+ static final int FILE_TYPE_DEX = 'D';
+
+ private static final String TAG = "PackageDynamicCodeLoading";
+
+ private static final String FILE_VERSION_HEADER = "DCL1";
+ private static final String PACKAGE_PREFIX = "P:";
+
+ private static final char FIELD_SEPARATOR = ':';
+ private static final String PACKAGE_SEPARATOR = ",";
+
+ /**
+ * Regular expression to match the expected format of an input line describing one file.
+ * <p>Example: {@code D:10:package.name1,package.name2:/escaped/path}
+ * <p>The capturing groups are the file type, user ID, loading packages and escaped file path
+ * (in that order).
+ * <p>See {@link #write(OutputStream, Map)} below for more details of the format.
+ */
+ private static final Pattern PACKAGE_LINE_PATTERN =
+ Pattern.compile("([A-Z]):([0-9]+):([^:]*):(.*)");
+
+ private final Object mLock = new Object();
+
+ // Map from package name to data about loading of dynamic code files owned by that package.
+ // (Apps may load code files owned by other packages, subject to various access
+ // constraints.)
+ // Any PackageDynamicCode in this map will be non-empty.
+ @GuardedBy("mLock")
+ private Map<String, PackageDynamicCode> mPackageMap = new HashMap<>();
+
+ PackageDynamicCodeLoading() {
+ super("package-dcl.list", "PackageDynamicCodeLoading_DiskWriter", false);
+ }
+
+ /**
+ * Record dynamic code loading from a file.
+ *
+ * Note this is called when an app loads dex files and as such it should return
+ * as fast as possible.
+ *
+ * @param owningPackageName the package owning the file path
+ * @param filePath the path of the dex files being loaded
+ * @param fileType the type of code loading
+ * @param ownerUserId the user id which runs the code loading the file
+ * @param loadingPackageName the package performing the load
+ * @return whether new information has been recorded
+ * @throws IllegalArgumentException if clearly invalid information is detected
+ */
+ boolean record(String owningPackageName, String filePath, int fileType, int ownerUserId,
+ String loadingPackageName) {
+ if (fileType != FILE_TYPE_DEX) {
+ throw new IllegalArgumentException("Bad file type: " + fileType);
+ }
+ synchronized (mLock) {
+ PackageDynamicCode packageInfo = mPackageMap.get(owningPackageName);
+ if (packageInfo == null) {
+ packageInfo = new PackageDynamicCode();
+ mPackageMap.put(owningPackageName, packageInfo);
+ }
+ return packageInfo.add(filePath, (char) fileType, ownerUserId, loadingPackageName);
+ }
+ }
+
+ /**
+ * Return all packages that contain records of secondary dex files. (Note that data updates
+ * asynchronously, so {@link #getPackageDynamicCodeInfo} may still return null if passed
+ * one of these package names.)
+ */
+ Set<String> getAllPackagesWithDynamicCodeLoading() {
+ synchronized (mLock) {
+ return new HashSet<>(mPackageMap.keySet());
+ }
+ }
+
+ /**
+ * Return information about the dynamic code file usage of the specified package,
+ * or null if there is currently no usage information. The object returned is a copy of the
+ * live information that is not updated.
+ */
+ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+ synchronized (mLock) {
+ PackageDynamicCode info = mPackageMap.get(packageName);
+ return info == null ? null : new PackageDynamicCode(info);
+ }
+ }
+
+ /**
+ * Remove all information about all packages.
+ */
+ void clear() {
+ synchronized (mLock) {
+ mPackageMap.clear();
+ }
+ }
+
+ /**
+ * Remove the data associated with package {@code packageName}. Affects all users.
+ * @return true if the package usage was found and removed successfully
+ */
+ boolean removePackage(String packageName) {
+ synchronized (mLock) {
+ return mPackageMap.remove(packageName) != null;
+ }
+ }
+
+ /**
+ * Remove all the records about package {@code packageName} belonging to user {@code userId}.
+ * @return whether any data was actually removed
+ */
+ boolean removeUserPackage(String packageName, int userId) {
+ synchronized (mLock) {
+ PackageDynamicCode packageDynamicCode = mPackageMap.get(packageName);
+ if (packageDynamicCode == null) {
+ return false;
+ }
+ if (packageDynamicCode.removeUser(userId)) {
+ if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+ mPackageMap.remove(packageName);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Remove the specified dynamic code file record belonging to the package {@code packageName}
+ * and user {@code userId}.
+ * @return whether data was actually removed
+ */
+ boolean removeFile(String packageName, String filePath, int userId) {
+ synchronized (mLock) {
+ PackageDynamicCode packageDynamicCode = mPackageMap.get(packageName);
+ if (packageDynamicCode == null) {
+ return false;
+ }
+ if (packageDynamicCode.removeFile(filePath, userId)) {
+ if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+ mPackageMap.remove(packageName);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Syncs data with the set of installed packages. Data about packages that are no longer
+ * installed is removed.
+ * @param packageToUsersMap a map from all existing package names to the users who have the
+ * package installed
+ */
+ void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+ synchronized (mLock) {
+ Iterator<Entry<String, PackageDynamicCode>> it = mPackageMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, PackageDynamicCode> entry = it.next();
+ Set<Integer> packageUsers = packageToUsersMap.get(entry.getKey());
+ if (packageUsers == null) {
+ it.remove();
+ } else {
+ PackageDynamicCode packageDynamicCode = entry.getValue();
+ packageDynamicCode.syncData(packageToUsersMap, packageUsers);
+ if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Request that data be written to persistent file at the next time allowed by write-limiting.
+ */
+ void maybeWriteAsync() {
+ super.maybeWriteAsync(null);
+ }
+
+ /**
+ * Writes data to persistent file immediately.
+ */
+ void writeNow() {
+ super.writeNow(null);
+ }
+
+ @Override
+ protected final void writeInternal(Void data) {
+ AtomicFile file = getFile();
+ FileOutputStream output = null;
+ try {
+ output = file.startWrite();
+ write(output);
+ file.finishWrite(output);
+ } catch (IOException e) {
+ file.failWrite(output);
+ Slog.e(TAG, "Failed to write dynamic usage for secondary code files.", e);
+ }
+ }
+
+ @VisibleForTesting
+ void write(OutputStream output) throws IOException {
+ // Make a deep copy to avoid holding the lock while writing to disk.
+ Map<String, PackageDynamicCode> copiedMap;
+ synchronized (mLock) {
+ copiedMap = new HashMap<>(mPackageMap.size());
+ for (Entry<String, PackageDynamicCode> entry : mPackageMap.entrySet()) {
+ PackageDynamicCode copiedValue = new PackageDynamicCode(entry.getValue());
+ copiedMap.put(entry.getKey(), copiedValue);
+ }
+ }
+
+ write(output, copiedMap);
+ }
+
+ /**
+ * Write the dynamic code loading data as a text file to {@code output}. The file format begins
+ * with a line indicating the file type and version - {@link #FILE_VERSION_HEADER}.
+ * <p>There is then one section for each owning package, introduced by a line beginning "P:".
+ * This is followed by a line for each file owned by the package this is dynamically loaded,
+ * containing the file type, user ID, loading package names and full path (with newlines and
+ * backslashes escaped - see {@link #escape}).
+ * <p>For example:
+ * <pre>{@code
+ * DCL1
+ * P:first.owning.package
+ * D:0:loading.package_1,loading.package_2:/path/to/file
+ * D:10:loading.package_1:/another/file
+ * P:second.owning.package
+ * D:0:loading.package:/third/file
+ * }</pre>
+ */
+ private static void write(OutputStream output, Map<String, PackageDynamicCode> packageMap)
+ throws IOException {
+ PrintWriter writer = new FastPrintWriter(output);
+
+ writer.println(FILE_VERSION_HEADER);
+ for (Entry<String, PackageDynamicCode> packageEntry : packageMap.entrySet()) {
+ writer.print(PACKAGE_PREFIX);
+ writer.println(packageEntry.getKey());
+
+ Map<String, DynamicCodeFile> mFileUsageMap = packageEntry.getValue().mFileUsageMap;
+ for (Entry<String, DynamicCodeFile> fileEntry : mFileUsageMap.entrySet()) {
+ String path = fileEntry.getKey();
+ DynamicCodeFile dynamicCodeFile = fileEntry.getValue();
+
+ writer.print(dynamicCodeFile.mFileType);
+ writer.print(FIELD_SEPARATOR);
+ writer.print(dynamicCodeFile.mUserId);
+ writer.print(FIELD_SEPARATOR);
+
+ String prefix = "";
+ for (String packageName : dynamicCodeFile.mLoadingPackages) {
+ writer.print(prefix);
+ writer.print(packageName);
+ prefix = PACKAGE_SEPARATOR;
+ }
+
+ writer.print(FIELD_SEPARATOR);
+ writer.println(escape(path));
+ }
+ }
+
+ writer.flush();
+ if (writer.checkError()) {
+ throw new IOException("Writer failed");
+ }
+ }
+
+ /**
+ * Read data from the persistent file. Replaces existing data completely if successful.
+ */
+ void read() {
+ super.read(null);
+ }
+
+ @Override
+ protected final void readInternal(Void data) {
+ AtomicFile file = getFile();
+
+ FileInputStream stream = null;
+ try {
+ stream = file.openRead();
+ read(stream);
+ } catch (FileNotFoundException expected) {
+ // The file may not be there. E.g. When we first take the OTA with this feature.
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to parse dynamic usage for secondary code files.", e);
+ } finally {
+ IoUtils.closeQuietly(stream);
+ }
+ }
+
+ @VisibleForTesting
+ void read(InputStream stream) throws IOException {
+ Map<String, PackageDynamicCode> newPackageMap = new HashMap<>();
+ read(stream, newPackageMap);
+ synchronized (mLock) {
+ mPackageMap = newPackageMap;
+ }
+ }
+
+ private static void read(InputStream stream, Map<String, PackageDynamicCode> packageMap)
+ throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+ String versionLine = reader.readLine();
+ if (!FILE_VERSION_HEADER.equals(versionLine)) {
+ throw new IOException("Incorrect version line: " + versionLine);
+ }
+
+ String line = reader.readLine();
+ if (line != null && !line.startsWith(PACKAGE_PREFIX)) {
+ throw new IOException("Malformed line: " + line);
+ }
+
+ while (line != null) {
+ String packageName = line.substring(PACKAGE_PREFIX.length());
+
+ PackageDynamicCode packageInfo = new PackageDynamicCode();
+ while (true) {
+ line = reader.readLine();
+ if (line == null || line.startsWith(PACKAGE_PREFIX)) {
+ break;
+ }
+ readFileInfo(line, packageInfo);
+ }
+
+ if (!packageInfo.mFileUsageMap.isEmpty()) {
+ packageMap.put(packageName, packageInfo);
+ }
+ }
+ }
+
+ private static void readFileInfo(String line, PackageDynamicCode output) throws IOException {
+ try {
+ Matcher matcher = PACKAGE_LINE_PATTERN.matcher(line);
+ if (!matcher.matches()) {
+ throw new IOException("Malformed line: " + line);
+ }
+
+ char type = matcher.group(1).charAt(0);
+ int user = Integer.parseInt(matcher.group(2));
+ String[] packages = matcher.group(3).split(PACKAGE_SEPARATOR);
+ String path = unescape(matcher.group(4));
+
+ if (packages.length == 0) {
+ throw new IOException("Malformed line: " + line);
+ }
+ if (type != FILE_TYPE_DEX) {
+ throw new IOException("Unknown file type: " + line);
+ }
+
+ output.mFileUsageMap.put(path, new DynamicCodeFile(type, user, packages));
+ } catch (RuntimeException e) {
+ // Just in case we get NumberFormatException, or various
+ // impossible out of bounds errors happen.
+ throw new IOException("Unable to parse line: " + line, e);
+ }
+ }
+
+ /**
+ * Escape any newline and backslash characters in path. A newline in a path is legal if unusual,
+ * and it would break our line-based file parsing.
+ */
+ @VisibleForTesting
+ static String escape(String path) {
+ if (path.indexOf('\\') == -1 && path.indexOf('\n') == -1 && path.indexOf('\r') == -1) {
+ return path;
+ }
+
+ StringBuilder result = new StringBuilder(path.length() + 10);
+ for (int i = 0; i < path.length(); i++) {
+ // Surrogates will never match the characters we care about, so it's ok to use chars
+ // not code points here.
+ char c = path.charAt(i);
+ switch (c) {
+ case '\\':
+ result.append("\\\\");
+ break;
+ case '\n':
+ result.append("\\n");
+ break;
+ case '\r':
+ result.append("\\r");
+ break;
+ default:
+ result.append(c);
+ break;
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Reverse the effect of {@link #escape}.
+ * @throws IOException if the input string is malformed
+ */
+ @VisibleForTesting
+ static String unescape(String escaped) throws IOException {
+ // As we move through the input string, start is the position of the first character
+ // after the previous escape sequence and finish is the position of the following backslash.
+ int start = 0;
+ int finish = escaped.indexOf('\\');
+ if (finish == -1) {
+ return escaped;
+ }
+
+ StringBuilder result = new StringBuilder(escaped.length());
+ while (true) {
+ if (finish >= escaped.length() - 1) {
+ // Backslash mustn't be the last character
+ throw new IOException("Unexpected \\ in: " + escaped);
+ }
+ result.append(escaped, start, finish);
+ switch (escaped.charAt(finish + 1)) {
+ case '\\':
+ result.append('\\');
+ break;
+ case 'r':
+ result.append('\r');
+ break;
+ case 'n':
+ result.append('\n');
+ break;
+ default:
+ throw new IOException("Bad escape in: " + escaped);
+ }
+
+ start = finish + 2;
+ finish = escaped.indexOf('\\', start);
+ if (finish == -1) {
+ result.append(escaped, start, escaped.length());
+ break;
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Represents the dynamic code usage of a single package.
+ */
+ static class PackageDynamicCode {
+ /**
+ * Map from secondary code file path to information about which packages dynamically load
+ * that file.
+ */
+ final Map<String, DynamicCodeFile> mFileUsageMap;
+
+ private PackageDynamicCode() {
+ mFileUsageMap = new HashMap<>();
+ }
+
+ private PackageDynamicCode(PackageDynamicCode original) {
+ mFileUsageMap = new HashMap<>(original.mFileUsageMap.size());
+ for (Entry<String, DynamicCodeFile> entry : original.mFileUsageMap.entrySet()) {
+ DynamicCodeFile newValue = new DynamicCodeFile(entry.getValue());
+ mFileUsageMap.put(entry.getKey(), newValue);
+ }
+ }
+
+ private boolean add(String path, char fileType, int userId, String loadingPackage) {
+ DynamicCodeFile fileInfo = mFileUsageMap.get(path);
+ if (fileInfo == null) {
+ fileInfo = new DynamicCodeFile(fileType, userId, loadingPackage);
+ mFileUsageMap.put(path, fileInfo);
+ return true;
+ } else {
+ if (fileInfo.mUserId != userId) {
+ // This should be impossible: private app files are always user-specific and
+ // can't be accessed from different users.
+ throw new IllegalArgumentException("Cannot change userId for '" + path
+ + "' from " + fileInfo.mUserId + " to " + userId);
+ }
+ // Changing file type (i.e. loading the same file in different ways is possible if
+ // unlikely. We allow it but ignore it.
+ return fileInfo.mLoadingPackages.add(loadingPackage);
+ }
+ }
+
+ private boolean removeUser(int userId) {
+ boolean updated = false;
+ Iterator<DynamicCodeFile> it = mFileUsageMap.values().iterator();
+ while (it.hasNext()) {
+ DynamicCodeFile fileInfo = it.next();
+ if (fileInfo.mUserId == userId) {
+ it.remove();
+ updated = true;
+ }
+ }
+ return updated;
+ }
+
+ private boolean removeFile(String filePath, int userId) {
+ DynamicCodeFile fileInfo = mFileUsageMap.get(filePath);
+ if (fileInfo == null || fileInfo.mUserId != userId) {
+ return false;
+ } else {
+ mFileUsageMap.remove(filePath);
+ return true;
+ }
+ }
+
+ private void syncData(Map<String, Set<Integer>> packageToUsersMap,
+ Set<Integer> owningPackageUsers) {
+ Iterator<DynamicCodeFile> fileIt = mFileUsageMap.values().iterator();
+ while (fileIt.hasNext()) {
+ DynamicCodeFile fileInfo = fileIt.next();
+ int fileUserId = fileInfo.mUserId;
+ if (!owningPackageUsers.contains(fileUserId)) {
+ fileIt.remove();
+ } else {
+ // Also remove information about any loading packages that are no longer
+ // installed for this user.
+ Iterator<String> loaderIt = fileInfo.mLoadingPackages.iterator();
+ while (loaderIt.hasNext()) {
+ String loader = loaderIt.next();
+ Set<Integer> loadingPackageUsers = packageToUsersMap.get(loader);
+ if (loadingPackageUsers == null
+ || !loadingPackageUsers.contains(fileUserId)) {
+ loaderIt.remove();
+ }
+ }
+ if (fileInfo.mLoadingPackages.isEmpty()) {
+ fileIt.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Represents a single dynamic code file loaded by one or more packages. Note that it is
+ * possible for one app to dynamically load code from a different app's home dir, if the
+ * owning app:
+ * <ul>
+ * <li>Targets API 27 or lower and has shared its home dir.
+ * <li>Is a system app.
+ * <li>Has a shared UID with the loading app.
+ * </ul>
+ */
+ static class DynamicCodeFile {
+ final char mFileType;
+ final int mUserId;
+ final Set<String> mLoadingPackages;
+
+ private DynamicCodeFile(char type, int user, String... packages) {
+ mFileType = type;
+ mUserId = user;
+ mLoadingPackages = new HashSet<>(Arrays.asList(packages));
+ }
+
+ private DynamicCodeFile(DynamicCodeFile original) {
+ mFileType = original.mFileType;
+ mUserId = original.mUserId;
+ mLoadingPackages = new HashSet<>(original.mLoadingPackages);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 2d583ca39adb..3a49412357d8 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -189,6 +189,12 @@ public final class BasePermission {
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}
+
+ public boolean isRemoved() {
+ return perm != null && perm.info != null
+ && (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0;
+ }
+
public boolean isSignature() {
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
PermissionInfo.PROTECTION_SIGNATURE;
@@ -235,6 +241,12 @@ public final class BasePermission {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
!= 0;
}
+ public boolean isWellbeing() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+ }
+ public boolean isDocumenter() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
+ }
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4406fdde6454..31f5ce47abd9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -799,6 +799,10 @@ public class PermissionManagerService {
continue;
}
+ if (bp.isRemoved()) {
+ continue;
+ }
+
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
@@ -1637,6 +1641,19 @@ public class PermissionManagerService {
// Special permissions for the system default text classifier.
allowed = true;
}
+ if (!allowed && bp.isWellbeing()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+ // Special permission granted only to the OEM specified wellbeing app
+ allowed = true;
+ }
+ if (!allowed && bp.isDocumenter()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
+ // If this permission is to be granted to the documenter and
+ // this app is the documenter, then it gets the permission.
+ allowed = true;
+ }
}
return allowed;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 29d62372e154..565bb706a5d8 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3221,6 +3221,20 @@ public final class PowerManagerService extends SystemService
mNativeWrapper.nativeSendPowerHint(hintId, data);
}
+ @VisibleForTesting
+ boolean wasDeviceIdleForInternal(long ms) {
+ synchronized (mLock) {
+ return mLastUserActivityTime + ms < SystemClock.uptimeMillis();
+ }
+ }
+
+ @VisibleForTesting
+ void onUserActivity() {
+ synchronized (mLock) {
+ mLastUserActivityTime = SystemClock.uptimeMillis();
+ }
+ }
+
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -4874,5 +4888,10 @@ public final class PowerManagerService extends SystemService
public void powerHint(int hintId, int data) {
powerHintInternal(hintId, data);
}
+
+ @Override
+ public boolean wasDeviceIdleFor(long ms) {
+ return wasDeviceIdleForInternal(ms);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 07bebad8e190..02689a90a98a 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -250,14 +250,29 @@ public class ThermalManagerService extends SystemService {
}
}
- private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
- synchronized (mLock) {
- // Thermal Shutdown for Skin temperature
- if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
- && temperature.getType() == Temperature.TYPE_SKIN) {
+ private void shutdownIfNeededLocked(Temperature temperature) {
+ if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) {
+ return;
+ }
+ switch (temperature.getType()) {
+ case Temperature.TYPE_CPU:
+ // Fall through
+ case Temperature.TYPE_GPU:
+ // Fall through
+ case Temperature.TYPE_NPU:
+ // Fall through
+ case Temperature.TYPE_SKIN:
mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
- }
+ break;
+ case Temperature.TYPE_BATTERY:
+ mPowerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
+ break;
+ }
+ }
+ private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
+ synchronized (mLock) {
+ shutdownIfNeededLocked(temperature);
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
if (old != null) {
if (old.getStatus() != temperature.getStatus()) {
@@ -300,6 +315,8 @@ public class ThermalManagerService extends SystemService {
final IThermalService.Stub mService = new IThermalService.Stub() {
@Override
public boolean registerThermalEventListener(IThermalEventListener listener) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -320,6 +337,8 @@ public class ThermalManagerService extends SystemService {
@Override
public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
int type) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -339,6 +358,8 @@ public class ThermalManagerService extends SystemService {
@Override
public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -351,6 +372,8 @@ public class ThermalManagerService extends SystemService {
@Override
public List<Temperature> getCurrentTemperatures() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady) {
@@ -364,6 +387,8 @@ public class ThermalManagerService extends SystemService {
@Override
public List<Temperature> getCurrentTemperaturesWithType(int type) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
final long token = Binder.clearCallingIdentity();
try {
if (!mHalReady) {
@@ -428,14 +453,15 @@ public class ThermalManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ pw.println("IsStatusOverride: " + mIsStatusOverride);
pw.println("ThermalEventListeners:");
mThermalEventListeners.dump(pw, "\t");
pw.println("ThermalStatusListeners:");
mThermalStatusListeners.dump(pw, "\t");
- pw.println("Thermal Status: " + Integer.toString(mStatus));
+ pw.println("Thermal Status: " + mStatus);
pw.println("Cached temperatures:");
dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
- pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+ pw.println("HAL Ready: " + mHalReady);
if (mHalReady) {
pw.println("HAL connection:");
mHalWrapper.dump(pw, "\t");
@@ -507,7 +533,7 @@ public class ThermalManagerService extends SystemService {
return -1;
}
if (!Temperature.isValidStatus(status)) {
- pw.println("Invalid status: " + Integer.toString(status));
+ pw.println("Invalid status: " + status);
return -1;
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 8711ddf58f25..35013de6a4eb 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -22,8 +22,10 @@ import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManager;
import android.app.role.IRoleManagerCallback;
import android.app.role.RoleManager;
@@ -33,6 +35,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.os.Handler;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
@@ -53,6 +58,8 @@ import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -73,7 +80,7 @@ import java.util.concurrent.TimeoutException;
*
* @see RoleManager
*/
-public class RoleManagerService extends SystemService {
+public class RoleManagerService extends SystemService implements RoleUserState.Callback {
private static final String LOG_TAG = RoleManagerService.class.getSimpleName();
@@ -100,6 +107,17 @@ public class RoleManagerService extends SystemService {
private final SparseArray<RemoteRoleControllerService> mControllerServices =
new SparseArray<>();
+ /**
+ * Maps user id to its list of listeners.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<RemoteCallbackList<IOnRoleHoldersChangedListener>> mListeners =
+ new SparseArray<>();
+
+ @NonNull
+ private final Handler mListenerHandler = FgThread.getHandler();
+
public RoleManagerService(@NonNull Context context) {
super(context);
@@ -188,7 +206,7 @@ public class RoleManagerService extends SystemService {
}
@Nullable
- private String computeComponentStateHash(@UserIdInt int userId) {
+ private static String computeComponentStateHash(@UserIdInt int userId) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -223,7 +241,7 @@ public class RoleManagerService extends SystemService {
synchronized (mLock) {
RoleUserState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new RoleUserState(userId);
+ userState = new RoleUserState(userId, this);
mUserStates.put(userId, userState);
}
return userState;
@@ -242,17 +260,70 @@ public class RoleManagerService extends SystemService {
}
}
+ @Nullable
+ private RemoteCallbackList<IOnRoleHoldersChangedListener> getListeners(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mListeners.get(userId);
+ }
+ }
+
+ @NonNull
+ private RemoteCallbackList<IOnRoleHoldersChangedListener> getOrCreateListeners(
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = mListeners.get(userId);
+ if (listeners == null) {
+ listeners = new RemoteCallbackList<>();
+ mListeners.put(userId, listeners);
+ }
+ return listeners;
+ }
+ }
+
private void onRemoveUser(@UserIdInt int userId) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners;
RoleUserState userState;
synchronized (mLock) {
+ listeners = mListeners.removeReturnOld(userId);
mControllerServices.remove(userId);
userState = mUserStates.removeReturnOld(userId);
}
+ if (listeners != null) {
+ listeners.kill();
+ }
if (userState != null) {
userState.destroy();
}
}
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ mListenerHandler.sendMessage(PooledLambda.obtainMessage(
+ RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
+ }
+
+ @WorkerThread
+ private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
+ if (listeners == null) {
+ return;
+ }
+
+ int broadcastCount = listeners.beginBroadcast();
+ try {
+ for (int i = 0; i < broadcastCount; i++) {
+ IOnRoleHoldersChangedListener listener = listeners.getBroadcastItem(i);
+ try {
+ listener.onRoleHoldersChanged(roleName, userId);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e);
+ }
+ }
+ } finally {
+ listeners.finishBroadcast();
+ }
+ }
+
private class Stub extends IRoleManager.Stub {
@Override
@@ -357,6 +428,42 @@ public class RoleManagerService extends SystemService {
}
@Override
+ public void addOnRoleHoldersChangedListenerAsUser(
+ @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
+ "addOnRoleHoldersChangedListenerAsUser");
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners(
+ userId);
+ listeners.register(listener);
+ }
+
+ @Override
+ public void removeOnRoleHoldersChangedListenerAsUser(
+ @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+ userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser");
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
+ "removeOnRoleHoldersChangedListenerAsUser");
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
+ if (listener == null) {
+ return;
+ }
+ listeners.unregister(listener);
+ }
+
+ @Override
public void setRoleNamesFromController(@NonNull List<String> roleNames) {
Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
getContext().enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index ec614a451f54..d55e261986d0 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -74,6 +74,9 @@ public class RoleUserState {
private final int mUserId;
@NonNull
+ private final Callback mCallback;
+
+ @NonNull
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -100,12 +103,14 @@ public class RoleUserState {
private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
/**
- * Create a new instance of user state, and read its state from disk if previously persisted.
+ * Create a new user state, and read its state from disk if previously persisted.
*
- * @param userId the user id for the new user state
+ * @param userId the user id for this user state
+ * @param callback the callback for this user state
*/
- public RoleUserState(@UserIdInt int userId) {
+ public RoleUserState(@UserIdInt int userId, @NonNull Callback callback) {
mUserId = userId;
+ mCallback = callback;
readFile();
}
@@ -116,6 +121,7 @@ public class RoleUserState {
public int getVersion() {
synchronized (mLock) {
throwIfDestroyedLocked();
+
return mVersion;
}
}
@@ -128,6 +134,7 @@ public class RoleUserState {
public void setVersion(int version) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
if (mVersion == version) {
return;
}
@@ -156,6 +163,7 @@ public class RoleUserState {
public void setPackagesHash(@Nullable String packagesHash) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
if (Objects.equals(mPackagesHash, packagesHash)) {
return;
}
@@ -174,6 +182,7 @@ public class RoleUserState {
public boolean isRoleAvailable(@NonNull String roleName) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
return mRoles.containsKey(roleName);
}
}
@@ -189,6 +198,7 @@ public class RoleUserState {
public ArraySet<String> getRoleHolders(@NonNull String roleName) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
return new ArraySet<>(mRoles.get(roleName));
}
}
@@ -201,29 +211,34 @@ public class RoleUserState {
public void setRoleNames(@NonNull List<String> roleNames) {
synchronized (mLock) {
throwIfDestroyedLocked();
+
boolean changed = false;
+
for (int i = mRoles.size() - 1; i >= 0; i--) {
String roleName = mRoles.keyAt(i);
+
if (!roleNames.contains(roleName)) {
ArraySet<String> packageNames = mRoles.valueAt(i);
if (!packageNames.isEmpty()) {
- Slog.e(LOG_TAG,
- "Holders of a removed role should have been cleaned up, role: "
- + roleName + ", holders: " + packageNames);
+ Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
+ + " role: " + roleName + ", holders: " + packageNames);
}
mRoles.removeAt(i);
changed = true;
}
}
+
int roleNamesSize = roleNames.size();
for (int i = 0; i < roleNamesSize; i++) {
String roleName = roleNames.get(i);
+
if (!mRoles.containsKey(roleName)) {
mRoles.put(roleName, new ArraySet<>());
Slog.i(LOG_TAG, "Added new role: " + roleName);
changed = true;
}
}
+
if (changed) {
scheduleWriteFileLocked();
}
@@ -241,20 +256,27 @@ public class RoleUserState {
*/
@CheckResult
public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+ boolean changed;
+
synchronized (mLock) {
throwIfDestroyedLocked();
+
ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
+ ", package: " + packageName);
return false;
}
- boolean changed = roleHolders.add(packageName);
+ changed = roleHolders.add(packageName);
if (changed) {
scheduleWriteFileLocked();
}
- return true;
}
+
+ if (changed) {
+ mCallback.onRoleHoldersChanged(roleName, mUserId);
+ }
+ return true;
}
/**
@@ -268,20 +290,28 @@ public class RoleUserState {
*/
@CheckResult
public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+ boolean changed;
+
synchronized (mLock) {
throwIfDestroyedLocked();
+
ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
+ ", package: " + packageName);
return false;
}
- boolean changed = roleHolders.remove(packageName);
+
+ changed = roleHolders.remove(packageName);
if (changed) {
scheduleWriteFileLocked();
}
- return true;
}
+
+ if (changed) {
+ mCallback.onRoleHoldersChanged(roleName, mUserId);
+ }
+ return true;
}
/**
@@ -520,8 +550,8 @@ public class RoleUserState {
}
/**
- * Destroy this state and delete the corresponding file. Any pending writes to the file will be
- * cancelled and any future interaction with this state will throw an exception.
+ * Destroy this user state and delete the corresponding file. Any pending writes to the file
+ * will be cancelled, and any future interaction with this state will throw an exception.
*/
public void destroy() {
synchronized (mLock) {
@@ -542,4 +572,18 @@ public class RoleUserState {
private static @NonNull File getFile(@UserIdInt int userId) {
return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
}
+
+ /**
+ * Callback for a user state.
+ */
+ public interface Callback {
+
+ /**
+ * Called when the holders of roles are changed.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param userId the user id for this role holder change
+ */
+ void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
+ }
}
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
new file mode 100644
index 000000000000..944db84acc71
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import android.os.Build;
+import android.util.Slog;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * Helper class for verifying config signatures.
+ */
+public class SignatureVerifier {
+
+ private static final String TAG = "SignedConfig";
+ private static final boolean DBG = false;
+
+ private static final String DEBUG_KEY =
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
+ + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+
+ private final PublicKey mDebugKey;
+
+ public SignatureVerifier() {
+ mDebugKey = createKey(DEBUG_KEY);
+ }
+
+ private static PublicKey createKey(String base64) {
+ EncodedKeySpec keySpec;
+ try {
+ byte[] key = Base64.getDecoder().decode(base64);
+ keySpec = new X509EncodedKeySpec(key);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to base64 decode public key", e);
+ return null;
+ }
+ try {
+ KeyFactory factory = KeyFactory.getInstance("EC");
+ return factory.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ Slog.e(TAG, "Failed to construct public key", e);
+ return null;
+ }
+ }
+
+ /**
+ * Verify a signature for signed config.
+ *
+ * @param config Config as read from APK meta-data.
+ * @param base64Signature Signature as read from APK meta-data.
+ * @return {@code true} iff the signature was successfully verified.
+ */
+ public boolean verifySignature(String config, String base64Signature)
+ throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ byte[] signature;
+ try {
+ signature = Base64.getDecoder().decode(base64Signature);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to base64 decode signature");
+ return false;
+ }
+ byte[] data = config.getBytes(StandardCharsets.UTF_8);
+ if (DBG) Slog.i(TAG, "Data: " + Base64.getEncoder().encodeToString(data));
+
+ if (Build.IS_DEBUGGABLE) {
+ if (mDebugKey != null) {
+ if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
+ Signature verifier = Signature.getInstance("SHA256withECDSA");
+ verifier.initVerify(mDebugKey);
+ verifier.update(data);
+ if (verifier.verify(signature)) {
+ Slog.i(TAG, "Verified config using debug key");
+ return true;
+ } else {
+ if (DBG) Slog.i(TAG, "Config verification failed using debug key");
+ }
+ } else {
+ Slog.w(TAG, "Debuggable build, but have no debug key");
+ }
+ }
+ // TODO verify production key.
+ Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION");
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfig.java b/services/core/java/com/android/server/signedconfig/SignedConfig.java
index a3f452c27d13..e6bb800045c8 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfig.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfig.java
@@ -48,7 +48,7 @@ public class SignedConfig {
private static final String CONFIG_KEY_VALUE = "value";
/**
- * Represents config values targetting to an SDK range.
+ * Represents config values targeting an SDK range.
*/
public static class PerSdkConfig {
public final int minSdk;
@@ -90,11 +90,24 @@ public class SignedConfig {
/**
* Parse configuration from an APK.
*
- * @param config config as read from the APK metadata.
+ * @param config Config string as read from the APK metadata.
+ * @param allowedKeys Set of allowed keys in the config. Any key/value mapping for a key not in
+ * this set will result in an {@link InvalidConfigException} being thrown.
+ * @param keyValueMappers Mappings for values per key. The keys in the top level map should be
+ * a subset of {@code allowedKeys}. The keys in the inner map indicate
+ * the set of allowed values for that keys value. This map will be
+ * applied to the value in the configuration. This is intended to allow
+ * enum-like values to be encoded as strings in the configuration, and
+ * mapped back to integers when the configuration is parsed.
+ *
+ * <p>Any config key with a value that does not appear in the
+ * corresponding map will result in an {@link InvalidConfigException}
+ * being thrown.
* @return Parsed configuration.
* @throws InvalidConfigException If there's a problem parsing the config.
*/
- public static SignedConfig parse(String config, Set<String> allowedKeys)
+ public static SignedConfig parse(String config, Set<String> allowedKeys,
+ Map<String, Map<String, String>> keyValueMappers)
throws InvalidConfigException {
try {
JSONObject json = new JSONObject(config);
@@ -103,7 +116,8 @@ public class SignedConfig {
JSONArray perSdkConfig = json.getJSONArray(KEY_CONFIG);
List<PerSdkConfig> parsedConfigs = new ArrayList<>();
for (int i = 0; i < perSdkConfig.length(); ++i) {
- parsedConfigs.add(parsePerSdkConfig(perSdkConfig.getJSONObject(i), allowedKeys));
+ parsedConfigs.add(parsePerSdkConfig(perSdkConfig.getJSONObject(i), allowedKeys,
+ keyValueMappers));
}
return new SignedConfig(version, parsedConfigs);
@@ -113,8 +127,17 @@ public class SignedConfig {
}
+ private static CharSequence quoted(Object s) {
+ if (s == null) {
+ return "null";
+ } else {
+ return "\"" + s + "\"";
+ }
+ }
+
@VisibleForTesting
- static PerSdkConfig parsePerSdkConfig(JSONObject json, Set<String> allowedKeys)
+ static PerSdkConfig parsePerSdkConfig(JSONObject json, Set<String> allowedKeys,
+ Map<String, Map<String, String>> keyValueMappers)
throws JSONException, InvalidConfigException {
int minSdk = json.getInt(CONFIG_KEY_MIN_SDK);
int maxSdk = json.getInt(CONFIG_KEY_MAX_SDK);
@@ -123,12 +146,23 @@ public class SignedConfig {
for (int i = 0; i < valueArray.length(); ++i) {
JSONObject keyValuePair = valueArray.getJSONObject(i);
String key = keyValuePair.getString(CONFIG_KEY_KEY);
- String value = keyValuePair.has(CONFIG_KEY_VALUE)
- ? keyValuePair.getString(CONFIG_KEY_VALUE)
+ Object valueObject = keyValuePair.has(CONFIG_KEY_VALUE)
+ ? keyValuePair.get(CONFIG_KEY_VALUE)
: null;
+ String value = valueObject == JSONObject.NULL || valueObject == null
+ ? null
+ : valueObject.toString();
if (!allowedKeys.contains(key)) {
throw new InvalidConfigException("Config key " + key + " is not allowed");
}
+ if (keyValueMappers.containsKey(key)) {
+ Map<String, String> mapper = keyValueMappers.get(key);
+ if (!mapper.containsKey(value)) {
+ throw new InvalidConfigException(
+ "Config key " + key + " contains unsupported value " + quoted(value));
+ }
+ value = mapper.get(value);
+ }
values.put(key, value);
}
return new PerSdkConfig(minSdk, maxSdk, values);
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
index 7ce071faab04..4908964109ad 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -17,12 +17,119 @@
package com.android.server.signedconfig;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
class SignedConfigApplicator {
- static void applyConfig(Context context, String config, String signature) {
- //TODO verify signature
- //TODO parse & apply config
+ private static final String TAG = "SignedConfig";
+
+ private static final Set<String> ALLOWED_KEYS = Collections.unmodifiableSet(new ArraySet<>(
+ Arrays.asList(
+ Settings.Global.HIDDEN_API_POLICY,
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS
+ )));
+
+ private static final Map<String, String> HIDDEN_API_POLICY_KEY_MAP = makeMap(
+ "DEFAULT", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT),
+ "DISABLED", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED),
+ "JUST_WARN", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_JUST_WARN),
+ "ENABLED", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED)
+ );
+
+ private static final Map<String, Map<String, String>> KEY_VALUE_MAPPERS = makeMap(
+ Settings.Global.HIDDEN_API_POLICY, HIDDEN_API_POLICY_KEY_MAP
+ );
+
+ private static <K, V> Map<K, V> makeMap(Object... keyValuePairs) {
+ if (keyValuePairs.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ final int len = keyValuePairs.length / 2;
+ ArrayMap<K, V> m = new ArrayMap<>(len);
+ for (int i = 0; i < len; ++i) {
+ m.put((K) keyValuePairs[i * 2], (V) keyValuePairs[(i * 2) + 1]);
+ }
+ return Collections.unmodifiableMap(m);
+
+ }
+
+ private final Context mContext;
+ private final String mSourcePackage;
+ private final SignatureVerifier mVerifier;
+
+ SignedConfigApplicator(Context context, String sourcePackage) {
+ mContext = context;
+ mSourcePackage = sourcePackage;
+ mVerifier = new SignatureVerifier();
}
+ private boolean checkSignature(String data, String signature) {
+ try {
+ return mVerifier.verifySignature(data, signature);
+ } catch (GeneralSecurityException e) {
+ Slog.e(TAG, "Failed to verify signature", e);
+ return false;
+ }
+ }
+
+ private int getCurrentConfigVersion() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SIGNED_CONFIG_VERSION, 0);
+ }
+
+ private void updateCurrentConfig(int version, Map<String, String> values) {
+ for (Map.Entry<String, String> e: values.entrySet()) {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ e.getKey(),
+ e.getValue());
+ }
+ Settings.Global.putInt(
+ mContext.getContentResolver(), Settings.Global.SIGNED_CONFIG_VERSION, version);
+ }
+
+
+ void applyConfig(String configStr, String signature) {
+ if (!checkSignature(configStr, signature)) {
+ Slog.e(TAG, "Signature check on signed configuration in package " + mSourcePackage
+ + " failed; ignoring");
+ return;
+ }
+ SignedConfig config;
+ try {
+ config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
+ } catch (InvalidConfigException e) {
+ Slog.e(TAG, "Failed to parse config from package " + mSourcePackage, e);
+ return;
+ }
+ int currentVersion = getCurrentConfigVersion();
+ if (currentVersion >= config.version) {
+ Slog.i(TAG, "Config from package " + mSourcePackage + " is older than existing: "
+ + config.version + "<=" + currentVersion);
+ return;
+ }
+ // We have new config!
+ Slog.i(TAG, "Got new signed config from package " + mSourcePackage + ": version "
+ + config.version + " replacing existing version " + currentVersion);
+ SignedConfig.PerSdkConfig matchedConfig =
+ config.getMatchingConfig(Build.VERSION.SDK_INT);
+ if (matchedConfig == null) {
+ Slog.i(TAG, "Config is not applicable to current SDK version; ignoring");
+ return;
+ }
+
+ Slog.i(TAG, "Updating signed config to version " + config.version);
+ updateCurrentConfig(config.version, matchedConfig.values);
+ }
}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 148568628397..be1d41dd392b 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -85,7 +85,8 @@ public class SignedConfigService {
Slog.d(TAG, "Got signed config: " + config);
Slog.d(TAG, "Got config signature: " + signature);
}
- SignedConfigApplicator.applyConfig(mContext, config, signature);
+ new SignedConfigApplicator(mContext, packageName).applyConfig(
+ config, signature);
} else {
if (DBG) Slog.d(TAG, "Package has no config/signature.");
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index c8a68b44c796..6b0419e0f7ad 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -23,7 +23,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -136,6 +135,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -158,6 +158,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -180,6 +181,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -199,7 +201,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
public void onSelectionEvent(
TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
Preconditions.checkNotNull(event);
- validateInput(event.getPackageName(), mContext);
+ validateInput(mContext, event.getPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -220,6 +222,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
ITextLanguageCallback callback) throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -242,6 +245,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
IConversationActionsCallback callback) throws RemoteException {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(callback);
+ validateInput(mContext, request.getCallingPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -263,7 +267,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
throws RemoteException {
Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(classificationContext);
- validateInput(classificationContext.getPackageName(), mContext);
+ validateInput(mContext, classificationContext.getPackageName());
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
@@ -398,15 +402,17 @@ public final class TextClassificationManagerService extends ITextClassifierServi
e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
}
- private static void validateInput(String packageName, Context context)
+ private static void validateInput(Context context, @Nullable String packageName)
throws RemoteException {
+ if (packageName == null) return;
+
try {
final int uid = context.getPackageManager()
.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
Preconditions.checkArgument(Binder.getCallingUid() == uid);
- } catch (IllegalArgumentException | NullPointerException |
- PackageManager.NameNotFoundException e) {
- throw new RemoteException(e.getMessage());
+ } catch (Exception e) {
+ throw new RemoteException(
+ String.format("Invalid package: name=%s, error=%s", packageName, e));
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 7683172815e9..aca9702a45c8 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1797,7 +1797,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// focus). Also if there is an active pinned stack - we always want to notify it about
// task stack changes, because its positioning may depend on it.
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
- || getDisplay().hasPinnedStack()) {
+ || (getDisplay() != null && getDisplay().hasPinnedStack())) {
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4538d361ccfe..57bfc2979636 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2506,8 +2506,9 @@ class ActivityStarter {
// full resolution.
mLaunchParams.mPreferredDisplayId =
mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
+ final boolean onTop = aOptions == null || !aOptions.getAvoidMoveToFront();
final ActivityStack stack =
- mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
+ mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams);
mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
return stack;
}
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index a335fa26dfcf..5b20af3534c4 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -219,7 +219,7 @@ public class BarController {
}
private boolean updateStateLw(final int state) {
- if (state != mState) {
+ if (mWin != null && state != mState) {
mState = state;
if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
mHandler.post(new Runnable() {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 639ed02a1e48..f9c9d33c561a 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,5 +1,6 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -9,7 +10,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
-import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -204,6 +204,37 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
+ /** Callback to get pointer display id. */
+ @Override
+ public int getPointerDisplayId() {
+ synchronized (mService.mGlobalLock) {
+ // If desktop mode is not enabled, show on the default display.
+ if (!mService.mForceDesktopModeOnExternalDisplays) {
+ return DEFAULT_DISPLAY;
+ }
+
+ // Look for the topmost freeform display.
+ int firstExternalDisplayId = DEFAULT_DISPLAY;
+ for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
+ // Heuristic solution here. Currently when "Freeform windows" developer option is
+ // enabled we automatically put secondary displays in freeform mode and emulating
+ // "desktop mode". It also makes sense to show the pointer on the same display.
+ if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ return displayContent.getDisplayId();
+ }
+
+ if (firstExternalDisplayId == DEFAULT_DISPLAY
+ && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
+ firstExternalDisplayId = displayContent.getDisplayId();
+ }
+ }
+
+ // Look for the topmost non-default display
+ return firstExternalDisplayId;
+ }
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 86fcf6455d71..42cd8e864322 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -107,7 +107,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
&& recentsComponent.equals(intent.getComponent())
? ACTIVITY_TYPE_RECENTS
: ACTIVITY_TYPE_HOME;
- final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+ ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
final boolean hasExistingActivity = targetActivity != null;
@@ -153,7 +153,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
"startRecentsActivity");
}
} else {
- // No recents activity
+ // No recents activity, create the new recents activity bottom most
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchActivityType(mTargetActivityType);
options.setAvoidMoveToFront();
@@ -166,11 +166,20 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
.setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
.setMayWait(userId)
.execute();
- mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
- mWindowManager.executeAppTransition();
+ // Move the recents activity into place for the animation
targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType).getTopActivity();
+ targetStack = targetActivity.getActivityStack();
+ mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
+ if (DEBUG) {
+ Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+ + mDefaultDisplay.getStackAbove(targetStack));
+ }
+
+ mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+ mWindowManager.executeAppTransition();
+
// TODO: Maybe wait for app to draw in this particular case?
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 43d2dcf7e0d1..bf83ca912eed 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@ static struct {
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
+ jmethodID getPointerDisplayId;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
jmethodID getTouchCalibrationForInputDevice;
@@ -174,15 +175,6 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
-static void updatePointerControllerFromViewport(
- sp<PointerController> controller, const DisplayViewport* const viewport) {
- if (controller != nullptr && viewport != nullptr) {
- const int32_t width = viewport->logicalRight - viewport->logicalLeft;
- const int32_t height = viewport->logicalBottom - viewport->logicalTop;
- controller->setDisplayViewport(width, height, viewport->orientation);
- }
-}
-
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -242,6 +234,7 @@ public:
jfloatArray matrixArr);
virtual TouchAffineTransformation getTouchAffineTransformation(
const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
+ virtual void updatePointerDisplay();
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -314,10 +307,11 @@ private:
std::atomic<bool> mInteractive;
- void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
+ void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
-
+ const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
+ int32_t getPointerDisplayId();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
@@ -391,9 +385,10 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c
return false;
}
-static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
- for (const DisplayViewport& v : viewports) {
- if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
+ REQUIRES(mLock) {
+ for (const DisplayViewport& v : mLocked.viewports) {
+ if (v.displayId == displayId) {
return &v;
}
}
@@ -420,20 +415,10 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
}
}
- const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
- {
+ { // acquire lock
AutoMutex _l(mLock);
- const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
- // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
- // if they are different.
- const bool internalViewportChanged = (newInternalViewport != nullptr) &&
- (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
- if (internalViewportChanged) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- updatePointerControllerFromViewport(controller, newInternalViewport);
- }
mLocked.viewports = viewports;
- }
+ } // release lock
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -556,13 +541,41 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
+ updateInactivityTimeoutLocked();
+ }
- const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
- updatePointerControllerFromViewport(controller, internalViewport);
+ return controller;
+}
- updateInactivityTimeoutLocked(controller);
+int32_t NativeInputManager::getPointerDisplayId() {
+ JNIEnv* env = jniEnv();
+ jint pointerDisplayId = env->CallIntMethod(mServiceObj,
+ gServiceClassInfo.getPointerDisplayId);
+ if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
+ pointerDisplayId = ADISPLAY_ID_DEFAULT;
+ }
+
+ return pointerDisplayId;
+}
+
+void NativeInputManager::updatePointerDisplay() {
+ ATRACE_CALL();
+
+ jint pointerDisplayId = getPointerDisplayId();
+
+ AutoMutex _l(mLock);
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != nullptr) {
+ const DisplayViewport* viewport = findDisplayViewportLocked(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);
+ }
}
- return controller;
}
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
@@ -821,16 +834,16 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
if (mLocked.systemUiVisibility != visibility) {
mLocked.systemUiVisibility = visibility;
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- updateInactivityTimeoutLocked(controller);
- }
+ updateInactivityTimeoutLocked();
}
}
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
- REQUIRES(mLock) {
+void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller == nullptr) {
+ return;
+ }
+
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1824,6 +1837,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
+ GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
+ "getPointerDisplayId", "()I");
+
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index c44f3062c271..240b8206baf6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -126,4 +126,14 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
return Collections.emptyList();
}
+
+ @Override
+ public boolean isManagedKiosk() {
+ return false;
+ }
+
+ @Override
+ public boolean isUnattendedManagedKiosk() {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bfbaac92a0de..bc550dc8bd12 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -448,6 +448,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
/**
+ * The amount of ms that a managed kiosk must go without user interaction to be considered
+ * unattended.
+ */
+ private static final int UNATTENDED_MANAGED_KIOSK_MS = 30000;
+
+ /**
* Strings logged with {@link
* com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}.
*/
@@ -5140,7 +5146,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mInjector.binderRestoreCallingIdentity(ident);
}
- mInjector.getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
+ getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
UserHandle.USER_SYSTEM, timeMs);
}
@@ -5159,7 +5165,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
policy.mLastMaximumTimeToLock = timeMs;
- mInjector.getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
+ getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
userId, policy.mLastMaximumTimeToLock);
}
@@ -13577,4 +13583,79 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return Collections.emptyList();
}
+
+ @Override
+ public boolean isManagedKiosk() {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceManageUsers();
+ long id = mInjector.binderClearCallingIdentity();
+ try {
+ return isManagedKioskInternal();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ @Override
+ public boolean isUnattendedManagedKiosk() {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceManageUsers();
+ long id = mInjector.binderClearCallingIdentity();
+ try {
+ return isManagedKioskInternal()
+ && getPowerManagerInternal().wasDeviceIdleFor(UNATTENDED_MANAGED_KIOSK_MS);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ /**
+ * Returns whether the device is currently being used as a publicly-accessible dedicated device.
+ * Assumes that feature checks and permission checks have already been performed, and that the
+ * calling identity has been cleared.
+ */
+ private boolean isManagedKioskInternal() throws RemoteException {
+ return mOwners.hasDeviceOwner()
+ && mInjector.getIActivityManager().getLockTaskModeState()
+ == ActivityManager.LOCK_TASK_MODE_LOCKED
+ && !isLockTaskFeatureEnabled(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO)
+ && !deviceHasKeyguard()
+ && !inEphemeralUserSession();
+ }
+
+ private boolean isLockTaskFeatureEnabled(int lockTaskFeature) throws RemoteException {
+ int lockTaskFeatures =
+ getUserData(mInjector.getIActivityManager().getCurrentUser().id).mLockTaskFeatures;
+ return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature;
+ }
+
+ private boolean deviceHasKeyguard() {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (mLockPatternUtils.isSecure(userInfo.id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean inEphemeralUserSession() {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (mInjector.getUserManager().isUserEphemeral(userInfo.id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private PowerManagerInternal getPowerManagerInternal() {
+ return mInjector.getPowerManagerInternal();
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2baef6af0c11..88f645defa6d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@ import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
-import android.hardware.display.ColorDisplayManager;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -1443,11 +1442,9 @@ public final class SystemServer {
mSystemServiceManager.startService(TwilightService.class);
traceEnd();
- if (ColorDisplayManager.isNightDisplayAvailable(context)) {
- traceBeginAndSlog("StartColorDisplay");
- mSystemServiceManager.startService(ColorDisplayService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartColorDisplay");
+ mSystemServiceManager.startService(ColorDisplayService.class);
+ traceEnd();
traceBeginAndSlog("StartJobScheduler");
mSystemServiceManager.startService(JobSchedulerService.class);
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index b01adc97eeae..96ef0ce45001 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -22,7 +22,10 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+import android.annotation.UserIdInt;
import android.app.Application;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -32,11 +35,14 @@ import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowBinder;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,6 +50,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
import java.io.File;
import java.io.FileDescriptor;
@@ -51,14 +59,19 @@ import java.io.PrintWriter;
/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBinder.class})
@Presubmit
public class BackupManagerServiceTest {
private static final String TEST_PACKAGE = "package";
private static final String TEST_TRANSPORT = "transport";
+ private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+
+ private ShadowContextWrapper mShadowContext;
@Mock private UserBackupManagerService mUserBackupManagerService;
private BackupManagerService mBackupManagerService;
private Context mContext;
+ @UserIdInt private int mUserId;
/** Initialize {@link BackupManagerService}. */
@Before
@@ -67,6 +80,8 @@ public class BackupManagerServiceTest {
Application application = RuntimeEnvironment.application;
mContext = application;
+ mShadowContext = shadowOf(application);
+ mUserId = NON_USER_SYSTEM;
mBackupManagerService =
new BackupManagerService(
application,
@@ -76,6 +91,15 @@ public class BackupManagerServiceTest {
}
/**
+ * Clean up and reset state that was created for testing {@link BackupManagerService}
+ * operations.
+ */
+ @After
+ public void tearDown() throws Exception {
+ ShadowBinder.reset();
+ }
+
+ /**
* Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
* This is specifically to prevent overloading the logs in production.
*/
@@ -274,11 +298,41 @@ public class BackupManagerServiceTest {
// ---------------------------------------------
// Settings tests
// ---------------------------------------------
+ /**
+ * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void setBackupEnabled_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.setBackupEnabled(mUserId, true));
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
+ * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
+ * the same as the target user id.
+ */
+ @Test
+ public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
+ ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.setBackupEnabled(mUserId, true);
+
+ verify(mUserBackupManagerService).setBackupEnabled(true);
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
- mBackupManagerService.setBackupEnabled(true);
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.setBackupEnabled(mUserId, true);
verify(mUserBackupManagerService).setBackupEnabled(true);
}
@@ -299,10 +353,25 @@ public class BackupManagerServiceTest {
verify(mUserBackupManagerService).setBackupProvisioned(true);
}
+ /**
+ * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.isBackupEnabled(mUserId));
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
- mBackupManagerService.isBackupEnabled();
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.isBackupEnabled(mUserId);
verify(mUserBackupManagerService).isBackupEnabled();
}
@@ -330,30 +399,81 @@ public class BackupManagerServiceTest {
verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
}
+ /**
+ * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testBackupNow_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.backupNow(mUserId));
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testBackupNow_callsBackupNowForUser() throws Exception {
- mBackupManagerService.backupNow();
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.backupNow(mUserId);
verify(mUserBackupManagerService).backupNow();
}
+ /**
+ * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
+ * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testRequestBackup_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
+ }
+
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
String[] packages = {TEST_PACKAGE};
IBackupObserver observer = mock(IBackupObserver.class);
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
- mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0);
+ mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
+ /* flags */ 0);
verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
}
+ /**
+ * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
+ * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testCancelBackups_withoutPermission_throwsSecurityException() {
+ mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ expectThrows(
+ SecurityException.class,
+ () -> mBackupManagerService.cancelBackups(mUserId));
+ }
+
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
- mBackupManagerService.cancelBackups();
+ mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+
+ mBackupManagerService.cancelBackups(mUserId);
verify(mUserBackupManagerService).cancelBackups();
}
diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
index 59e9a15efb53..aa2a96e6fad4 100644
--- a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
+++ b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
@@ -16,6 +16,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
/**
* Unit tests for {@link GnssNavigationMessageProvider}.
@@ -33,8 +34,8 @@ public class GnssNavigationMessageProviderTest {
when(mMockNative.startNavigationMessageCollection()).thenReturn(true);
when(mMockNative.stopNavigationMessageCollection()).thenReturn(true);
- mTestProvider = new GnssNavigationMessageProvider(new Handler(Looper.myLooper()),
- mMockNative) {
+ mTestProvider = new GnssNavigationMessageProvider(RuntimeEnvironment.application,
+ new Handler(Looper.myLooper()), mMockNative) {
@Override
public boolean isGpsEnabled() {
return true;
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java
index 1ece49ea2060..9de9f502d843 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java
@@ -17,9 +17,11 @@
package com.android.server.testing.shadows;
import android.os.Binder;
+import android.os.UserHandle;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
/**
* Extends {@link org.robolectric.shadows.ShadowBinder} with {@link Binder#clearCallingIdentity()}
@@ -30,6 +32,7 @@ import org.robolectric.annotation.Implements;
public class ShadowBinder extends org.robolectric.shadows.ShadowBinder {
public static final Integer LOCAL_UID = 1000;
private static Integer originalCallingUid;
+ private static UserHandle sCallingUserHandle;
@Implementation
protected static long clearCallingIdentity() {
@@ -42,4 +45,30 @@ public class ShadowBinder extends org.robolectric.shadows.ShadowBinder {
protected static void restoreCallingIdentity(long token) {
setCallingUid(originalCallingUid);
}
+
+ public static void setCallingUserHandle(UserHandle userHandle) {
+ sCallingUserHandle = userHandle;
+ }
+
+ /**
+ * Shadows {@link Binder#getCallingUserHandle()}. If {@link ShadowBinder#sCallingUserHandle}
+ * is set, return that; otherwise mimic the default implementation.
+ */
+ @Implementation
+ public static UserHandle getCallingUserHandle() {
+ if (sCallingUserHandle != null) {
+ return sCallingUserHandle;
+ } else {
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+ }
+ }
+
+ /**
+ * Clean up and reset state that was created for testing.
+ */
+ @Resetter
+ public static void reset() {
+ sCallingUserHandle = null;
+ org.robolectric.shadows.ShadowBinder.reset();
+ }
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d49e78a20db5..d7b1cb475bb4 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -88,7 +88,7 @@ java_library {
"utils/**/*.java",
],
static_libs: [
- "android-support-test",
+ "junit",
"mockito-target-minus-junit4",
],
libs: [
diff --git a/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
index f70efdfadfd7..f75617ec5200 100644
--- a/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
@@ -18,7 +18,6 @@ package com.android.server;
import static org.junit.Assert.assertEquals;
-import android.os.Binder;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
@@ -26,9 +25,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.os.BinderInternal;
-import com.android.internal.os.BinderInternal.CallSession;
-import com.android.server.BinderCallsStatsService.WorkSourceProvider;
+import com.android.server.BinderCallsStatsService.AuthorizedWorkSourceProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +36,7 @@ import org.junit.runner.RunWith;
public class BinderCallsStatsServiceTest {
@Test
public void weTrustOurselves() {
- WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+ AuthorizedWorkSourceProvider workSourceProvider = new AuthorizedWorkSourceProvider() {
protected int getCallingUid() {
return Process.myUid();
}
@@ -55,7 +52,7 @@ public class BinderCallsStatsServiceTest {
@Test
public void workSourceSetIfCallerHasPermission() {
- WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+ AuthorizedWorkSourceProvider workSourceProvider = new AuthorizedWorkSourceProvider() {
protected int getCallingUid() {
// System process uid which as UPDATE_DEVICE_STATS.
return 1001;
@@ -72,7 +69,7 @@ public class BinderCallsStatsServiceTest {
@Test
public void workSourceResolvedToCallingUid() {
- WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+ AuthorizedWorkSourceProvider workSourceProvider = new AuthorizedWorkSourceProvider() {
protected int getCallingUid() {
// UID without permissions.
return Integer.MAX_VALUE;
@@ -86,40 +83,4 @@ public class BinderCallsStatsServiceTest {
assertEquals(Integer.MAX_VALUE, workSourceProvider.resolveWorkSourceUid());
}
-
- @Test
- public void workSourceSet() {
- TestObserver observer = new TestObserver();
- observer.callStarted(new Binder(), 0);
- assertEquals(true, observer.workSourceSet);
- }
-
- static class TestObserver extends BinderCallsStatsService.BinderCallsObserver {
- public boolean workSourceSet = false;
-
- TestObserver() {
- super(new NoopObserver(), new WorkSourceProvider());
- }
-
- @Override
- protected void setThreadLocalWorkSourceUid(int uid) {
- workSourceSet = true;
- }
- }
-
-
- static class NoopObserver implements BinderInternal.Observer {
- @Override
- public CallSession callStarted(Binder binder, int code) {
- return null;
- }
-
- @Override
- public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
- }
-
- @Override
- public void callThrewException(CallSession s, Exception exception) {
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index fd010f1fed4f..751ed9b1bd15 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -86,8 +87,9 @@ public class TrampolineTest {
new ComponentName("package1", "class1"),
new ComponentName("package2", "class2")
};
- private final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+ private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+ @UserIdInt private int mUserId;
@Mock private BackupManagerService mBackupManagerServiceMock;
@Mock private Context mContextMock;
@Mock private File mSuppressFileMock;
@@ -110,11 +112,13 @@ public class TrampolineTest {
TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
TrampolineTestable.sSuppressFile = mSuppressFileMock;
+ TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
TrampolineTestable.sBackupDisabled = false;
when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
+ mUserId = NON_USER_SYSTEM;
mTrampoline = new TrampolineTestable(mContextMock);
mContentResolver = new MockContentResolver();
@@ -361,10 +365,22 @@ public class TrampolineTest {
}
@Test
- public void setBackupEnabled_forwarded() throws RemoteException {
+ public void setBackupEnabledForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.setBackupEnabledForUser(mUserId, true);
+
+ verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
+ }
+
+ @Test
+ public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.setBackupEnabled(true);
- verify(mBackupManagerServiceMock).setBackupEnabled(true);
+
+ verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
}
@Test
@@ -400,10 +416,22 @@ public class TrampolineTest {
}
@Test
- public void isBackupEnabled_forwarded() throws RemoteException {
+ public void isBackupEnabledForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.isBackupEnabledForUser(mUserId);
+
+ verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
+ }
+
+ @Test
+ public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.isBackupEnabled();
- verify(mBackupManagerServiceMock).isBackupEnabled();
+
+ verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
}
@Test
@@ -439,10 +467,22 @@ public class TrampolineTest {
}
@Test
- public void backupNow_forwarded() throws RemoteException {
+ public void backupNowForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.backupNowForUser(mUserId);
+
+ verify(mBackupManagerServiceMock).backupNow(mUserId);
+ }
+
+ @Test
+ public void backupNow_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.backupNow();
- verify(mBackupManagerServiceMock).backupNow();
+
+ verify(mBackupManagerServiceMock).backupNow(mUserId);
}
@Test
@@ -798,15 +838,28 @@ public class TrampolineTest {
}
@Test
- public void requestBackup_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.requestBackup(PACKAGE_NAMES, mBackupObserverMock,
- mBackupManagerMonitorMock, 123)).thenReturn(456);
+ public void requestBackupForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123));
+ verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123);
+ }
+
+ @Test
+ public void requestBackup_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES, mBackupObserverMock,
- mBackupManagerMonitorMock, 123));
- verify(mBackupManagerServiceMock).requestBackup(PACKAGE_NAMES, mBackupObserverMock,
- mBackupManagerMonitorMock, 123);
+
+ assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123));
+ verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
+ mBackupObserverMock, mBackupManagerMonitorMock, 123);
}
@Test
@@ -816,10 +869,22 @@ public class TrampolineTest {
}
@Test
- public void cancelBackups_forwarded() throws RemoteException {
+ public void cancelBackupsForUser_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ mTrampoline.cancelBackupsForUser(mUserId);
+
+ verify(mBackupManagerServiceMock).cancelBackups(mUserId);
+ }
+
+ @Test
+ public void cancelBackups_forwardedToCallingUserId() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
mTrampoline.cancelBackups();
- verify(mBackupManagerServiceMock).cancelBackups();
+
+ verify(mBackupManagerServiceMock).cancelBackups(mUserId);
}
@Test
@@ -891,6 +956,7 @@ public class TrampolineTest {
private static class TrampolineTestable extends Trampoline {
static boolean sBackupDisabled = false;
static File sSuppressFile = null;
+ static int sCallingUserId = -1;
static int sCallingUid = -1;
static BackupManagerService sBackupManagerServiceMock = null;
private int mCreateServiceCallsCount = 0;
@@ -909,6 +975,10 @@ public class TrampolineTest {
return sSuppressFile;
}
+ protected int binderGetCallingUserId() {
+ return sCallingUserId;
+ }
+
@Override
protected int binderGetCallingUid() {
return sCallingUid;
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index c4c2ad926954..839b25f8491d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -212,7 +212,29 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PRIMARY_USER)));
+ UserHandle.of(PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_currentUser() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -234,7 +256,31 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_notInstalled() {
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -254,7 +300,29 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_TWO,
ACTIVITY_COMPONENT,
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_fakeCaller() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_TWO,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -276,7 +344,31 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_notExported() {
+ mActivityInfo.exported = false;
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -296,7 +388,29 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_ONE,
new ComponentName(PACKAGE_TWO, "test"),
- UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_profile_anotherPackage() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ new ComponentName(PACKAGE_TWO, "test"),
+ UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -316,7 +430,29 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(SECONDARY_USER)));
+ UserHandle.of(SECONDARY_USER).getIdentifier(),
+ true));
+
+ verify(mActivityTaskManagerInternal, never())
+ .startActivityAsUser(
+ nullable(IApplicationThread.class),
+ anyString(),
+ any(Intent.class),
+ nullable(Bundle.class),
+ anyInt());
+ }
+
+ @Test
+ public void startAnyActivityAsUser_secondaryUser() {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ mIApplicationThread,
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ UserHandle.of(SECONDARY_USER).getIdentifier(),
+ false));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -335,7 +471,8 @@ public class CrossProfileAppsServiceImplTest {
mIApplicationThread,
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- UserHandle.of(PRIMARY_USER));
+ UserHandle.of(PRIMARY_USER).getIdentifier(),
+ true);
verify(mActivityTaskManagerInternal)
.startActivityAsUser(
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index dad7b93e822e..fd07cb046fb5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -18,10 +18,14 @@ package com.android.server.pm.dex;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -41,6 +45,10 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.pm.Installer;
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -50,10 +58,6 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
-import dalvik.system.DelegateLastClassLoader;
-import dalvik.system.PathClassLoader;
-import dalvik.system.VMRuntime;
-
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -129,6 +133,9 @@ public class DexManagerTests {
// Package is not used by others, so we should get nothing back.
assertNoUseInfo(mFooUser0);
+
+ // A package loading its own code is not stored as DCL.
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -140,6 +147,8 @@ public class DexManagerTests {
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+ assertHasDclInfo(mBarUser0, mFooUser0, mBarUser0.getBaseAndSplitDexPaths());
}
@Test
@@ -152,6 +161,8 @@ public class DexManagerTests {
assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
}
@Test
@@ -164,6 +175,8 @@ public class DexManagerTests {
assertIsUsedByOtherApps(mBarUser0, pui, false);
assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
+
+ assertHasDclInfo(mBarUser0, mFooUser0, barSecondaries);
}
@Test
@@ -200,9 +213,10 @@ public class DexManagerTests {
}
@Test
- public void testPackageUseInfoNotFound() {
+ public void testNoNotify() {
// Assert we don't get back data we did not previously record.
assertNoUseInfo(mFooUser0);
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -210,6 +224,7 @@ public class DexManagerTests {
// Notifying with an invalid ISA should be ignored.
notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
assertNoUseInfo(mInvalidIsa);
+ assertNoDclInfo(mInvalidIsa);
}
@Test
@@ -218,6 +233,7 @@ public class DexManagerTests {
// register in DexManager#load should be ignored.
notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
assertNoUseInfo(mDoesNotExist);
+ assertNoDclInfo(mDoesNotExist);
}
@Test
@@ -226,6 +242,8 @@ public class DexManagerTests {
// Request should be ignored.
notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
assertNoUseInfo(mBarUser1);
+
+ assertNoDclInfo(mBarUser1);
}
@Test
@@ -235,6 +253,10 @@ public class DexManagerTests {
// still check that nothing goes unexpected in DexManager.
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
assertNoUseInfo(mBarUser1);
+ assertNoUseInfo(mFooUser0);
+
+ assertNoDclInfo(mBarUser1);
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -247,6 +269,7 @@ public class DexManagerTests {
// is trying to load something from it we should not find it.
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
assertNoUseInfo(newPackage);
+ assertNoDclInfo(newPackage);
// Notify about newPackage install and let mFoo load its dexes.
mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -257,6 +280,7 @@ public class DexManagerTests {
assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
+ assertHasDclInfo(newPackage, mFooUser0, newSecondaries);
}
@Test
@@ -273,6 +297,7 @@ public class DexManagerTests {
assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
+ assertHasDclInfo(newPackage, newPackage, newSecondaries);
}
@Test
@@ -305,6 +330,7 @@ public class DexManagerTests {
// We shouldn't find yet the new split as we didn't notify the package update.
notifyDexLoad(mFooUser0, newSplits, mUser0);
assertNoUseInfo(mBarUser0);
+ assertNoDclInfo(mBarUser0);
// Notify that bar is updated. splitSourceDirs will contain the updated path.
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -314,8 +340,8 @@ public class DexManagerTests {
// Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
notifyDexLoad(mFooUser0, newSplits, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
assertIsUsedByOtherApps(newSplits, pui, true);
+ assertHasDclInfo(mBarUser0, mFooUser0, newSplits);
}
@Test
@@ -326,11 +352,15 @@ public class DexManagerTests {
mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
- // Bar should not be around since it was removed for all users.
+ // Data for user 1 should still be present
PackageUseInfo pui = getPackageUseInfo(mBarUser1);
- assertNotNull(pui);
assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
/*isUsedByOtherApps*/false, mUser1);
+ assertHasDclInfo(mBarUser1, mBarUser1, mBarUser1.getSecondaryDexPaths());
+
+ // But not user 0
+ assertNoUseInfo(mBarUser0, mUser0);
+ assertNoDclInfo(mBarUser0, mUser0);
}
@Test
@@ -349,6 +379,8 @@ public class DexManagerTests {
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
assertIsUsedByOtherApps(mFooUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -362,6 +394,7 @@ public class DexManagerTests {
// Foo should not be around since all its secondary dex info were deleted
// and it is not used by other apps.
assertNoUseInfo(mFooUser0);
+ assertNoDclInfo(mFooUser0);
}
@Test
@@ -374,6 +407,7 @@ public class DexManagerTests {
// Bar should not be around since it was removed for all users.
assertNoUseInfo(mBarUser0);
+ assertNoDclInfo(mBarUser0);
}
@Test
@@ -383,6 +417,7 @@ public class DexManagerTests {
notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
// The dex file should not be recognized as a package.
assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
+ assertNull(mDexManager.getPackageDynamicCodeInfo(frameworkDex));
}
@Test
@@ -395,6 +430,8 @@ public class DexManagerTests {
assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
}
@Test
@@ -402,7 +439,12 @@ public class DexManagerTests {
List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
+ // We don't record the dex usage
assertNoUseInfo(mBarUser0UnsupportedClassLoader);
+
+ // But we do record this as an intance of dynamic code loading
+ assertHasDclInfo(
+ mBarUser0UnsupportedClassLoader, mBarUser0UnsupportedClassLoader, secondaries);
}
@Test
@@ -414,6 +456,8 @@ public class DexManagerTests {
notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0);
assertNoUseInfo(mBarUser0);
+
+ assertHasDclInfo(mBarUser0, mBarUser0, mBarUser0.getSecondaryDexPaths());
}
@Test
@@ -421,6 +465,7 @@ public class DexManagerTests {
notifyDexLoad(mBarUser0, null, mUser0);
assertNoUseInfo(mBarUser0);
+ assertNoDclInfo(mBarUser0);
}
@Test
@@ -455,12 +500,14 @@ public class DexManagerTests {
notifyDexLoad(mBarUser0, secondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+ assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
// Record bar secondaries again with an unsupported class loader. This should not change the
// context.
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
pui = getPackageUseInfo(mBarUser0);
assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+ assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
}
@Test
@@ -533,13 +580,53 @@ public class DexManagerTests {
private PackageUseInfo getPackageUseInfo(TestData testData) {
assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
- return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
+ PackageUseInfo pui = mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
+ assertNotNull(pui);
+ return pui;
}
private void assertNoUseInfo(TestData testData) {
assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
}
+ private void assertNoUseInfo(TestData testData, int userId) {
+ if (!mDexManager.hasInfoOnPackage(testData.getPackageName())) {
+ return;
+ }
+ PackageUseInfo pui = getPackageUseInfo(testData);
+ for (DexUseInfo dexUseInfo : pui.getDexUseInfoMap().values()) {
+ assertNotEquals(userId, dexUseInfo.getOwnerUserId());
+ }
+ }
+
+ private void assertNoDclInfo(TestData testData) {
+ assertNull(mDexManager.getPackageDynamicCodeInfo(testData.getPackageName()));
+ }
+
+ private void assertNoDclInfo(TestData testData, int userId) {
+ PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(testData.getPackageName());
+ if (info == null) {
+ return;
+ }
+
+ for (DynamicCodeFile fileInfo : info.mFileUsageMap.values()) {
+ assertNotEquals(userId, fileInfo.mUserId);
+ }
+ }
+
+ private void assertHasDclInfo(TestData owner, TestData loader, List<String> paths) {
+ PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(owner.getPackageName());
+ assertNotNull("No DCL data for owner " + owner.getPackageName(), info);
+ for (String path : paths) {
+ DynamicCodeFile fileInfo = info.mFileUsageMap.get(path);
+ assertNotNull("No DCL data for path " + path, fileInfo);
+ assertEquals(PackageDynamicCodeLoading.FILE_TYPE_DEX, fileInfo.mFileType);
+ assertEquals(owner.mUserId, fileInfo.mUserId);
+ assertTrue("No DCL data for loader " + loader.getPackageName(),
+ fileInfo.mLoadingPackages.contains(loader.getPackageName()));
+ }
+ }
+
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = packageName;
@@ -563,11 +650,13 @@ public class DexManagerTests {
private final PackageInfo mPackageInfo;
private final String mLoaderIsa;
private final String mClassLoader;
+ private final int mUserId;
private TestData(String packageName, String loaderIsa, int userId, String classLoader) {
mPackageInfo = getMockPackageInfo(packageName, userId);
mLoaderIsa = loaderIsa;
mClassLoader = classLoader;
+ mUserId = userId;
}
private TestData(String packageName, String loaderIsa, int userId) {
@@ -603,9 +692,7 @@ public class DexManagerTests {
List<String> getBaseAndSplitDexPaths() {
List<String> paths = new ArrayList<>();
paths.add(mPackageInfo.applicationInfo.sourceDir);
- for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
- paths.add(split);
- }
+ Collections.addAll(paths, mPackageInfo.applicationInfo.splitSourceDirs);
return paths;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
new file mode 100644
index 000000000000..eb4cc4e36616
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.escape;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.unescape;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PackageDynamicCodeLoadingTests {
+
+ // Deliberately making a copy here since we're testing identity and
+ // string literals have a tendency to be identical.
+ private static final String TRIVIAL_STRING = new String("hello/world");
+ private static final Entry[] NO_ENTRIES = {};
+ private static final String[] NO_PACKAGES = {};
+
+ @Test
+ public void testRecord() {
+ Entry[] entries = {
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+ new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+ new Entry("owning.package2", "/path/file3", 'D', 0, "loading.package2"),
+ };
+
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRecord_returnsHasChanged() {
+ Entry owner1Path1Loader1 =
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1");
+ Entry owner1Path1Loader2 =
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2");
+ Entry owner1Path2Loader1 =
+ new Entry("owning.package1", "/path/file2", 'D', 10, "loading.package1");
+ Entry owner2Path1Loader1 =
+ new Entry("owning.package2", "/path/file1", 'D', 10, "loading.package2");
+
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+
+ assertTrue(record(info, owner1Path1Loader1));
+ assertFalse(record(info, owner1Path1Loader1));
+
+ assertTrue(record(info, owner1Path1Loader2));
+ assertFalse(record(info, owner1Path1Loader2));
+
+ assertTrue(record(info, owner1Path2Loader1));
+ assertFalse(record(info, owner1Path2Loader1));
+
+ assertTrue(record(info, owner2Path1Loader1));
+ assertFalse(record(info, owner2Path1Loader1));
+
+ assertHasEntries(info,
+ owner1Path1Loader1, owner1Path1Loader2, owner1Path2Loader1, owner2Path1Loader1);
+ }
+
+ @Test
+ public void testRecord_changeUserForFile_throws() {
+ Entry entry1 = new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1");
+ Entry entry2 = new Entry("owning.package1", "/path/file1", 'D', 20, "loading.package1");
+
+ PackageDynamicCodeLoading info = makePackageDcl(entry1);
+
+ assertThrows(() -> record(info, entry2));
+ assertHasEntries(info, entry1);
+ }
+
+ @Test
+ public void testRecord_badFileType_throws() {
+ Entry entry = new Entry("owning.package", "/path/file", 'Z', 10, "loading.package");
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+
+ assertThrows(() -> record(info, entry));
+ }
+
+ @Test
+ public void testClear() {
+ Entry[] entries = {
+ new Entry("owner1", "file1", 'D', 10, "loader1"),
+ new Entry("owner2", "file2", 'D', 20, "loader2"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ info.clear();
+ assertHasEntries(info, NO_ENTRIES);
+ }
+
+ @Test
+ public void testRemovePackage_present() {
+ Entry other = new Entry("other", "file", 'D', 0, "loader");
+ Entry[] entries = {
+ new Entry("owner", "file1", 'D', 10, "loader1"),
+ new Entry("owner", "file2", 'D', 20, "loader2"),
+ other
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removePackage("owner"));
+ assertHasEntries(info, other);
+ assertHasPackages(info, "other");
+ }
+
+ @Test
+ public void testRemovePackage_notPresent() {
+ Entry[] entries = { new Entry("owner", "file", 'D', 0, "loader") };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removePackage("other"));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRemoveUserPackage_notPresent() {
+ Entry[] entries = { new Entry("owner", "file", 'D', 0, "loader") };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removeUserPackage("other", 0));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRemoveUserPackage_presentWithNoOtherUsers() {
+ Entry other = new Entry("other", "file", 'D', 0, "loader");
+ Entry[] entries = {
+ new Entry("owner", "file1", 'D', 0, "loader1"),
+ new Entry("owner", "file2", 'D', 0, "loader2"),
+ other
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeUserPackage("owner", 0));
+ assertHasEntries(info, other);
+ assertHasPackages(info, "other");
+ }
+
+ @Test
+ public void testRemoveUserPackage_presentWithUsers() {
+ Entry other = new Entry("owner", "file", 'D', 1, "loader");
+ Entry[] entries = {
+ new Entry("owner", "file1", 'D', 0, "loader1"),
+ new Entry("owner", "file2", 'D', 0, "loader2"),
+ other
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeUserPackage("owner", 0));
+ assertHasEntries(info, other);
+ }
+
+ @Test
+ public void testRemoveFile_present() {
+ Entry[] entries = {
+ new Entry("package1", "file1", 'D', 0, "loader1"),
+ new Entry("package1", "file2", 'D', 0, "loader1"),
+ new Entry("package2", "file1", 'D', 0, "loader2"),
+ };
+ Entry[] expectedSurvivors = {
+ new Entry("package1", "file2", 'D', 0, "loader1"),
+ new Entry("package2", "file1", 'D', 0, "loader2"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, expectedSurvivors);
+ }
+
+ @Test
+ public void testRemoveFile_onlyEntry() {
+ Entry[] entries = {
+ new Entry("package1", "file1", 'D', 0, "loader1"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertTrue(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, NO_ENTRIES);
+ assertHasPackages(info, NO_PACKAGES);
+ }
+
+ @Test
+ public void testRemoveFile_notPresent() {
+ Entry[] entries = {
+ new Entry("package1", "file2", 'D', 0, "loader1"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testRemoveFile_wrongUser() {
+ Entry[] entries = {
+ new Entry("package1", "file1", 'D', 10, "loader1"),
+ };
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+ assertFalse(info.removeFile("package1", "file1", 0));
+ assertHasEntries(info, entries);
+ }
+
+ @Test
+ public void testSyncData() {
+ Map<String, Set<Integer>> packageToUsersMap = ImmutableMap.of(
+ "package1", ImmutableSet.of(10, 20),
+ "package2", ImmutableSet.of(20));
+
+ Entry[] entries = {
+ new Entry("deleted.packaged", "file1", 'D', 10, "package1"),
+ new Entry("package1", "file2", 'D', 20, "package2"),
+ new Entry("package1", "file3", 'D', 10, "package2"),
+ new Entry("package1", "file3", 'D', 10, "deleted.package"),
+ new Entry("package2", "file4", 'D', 20, "deleted.package"),
+ };
+
+ Entry[] expectedSurvivors = {
+ new Entry("package1", "file2", 'D', 20, "package2"),
+ };
+
+ PackageDynamicCodeLoading info = makePackageDcl(entries);
+ info.syncData(packageToUsersMap);
+ assertHasEntries(info, expectedSurvivors);
+ assertHasPackages(info, "package1");
+ }
+
+ @Test
+ public void testRead_onlyHeader_emptyResult() throws Exception {
+ assertHasEntries(read("DCL1"), NO_ENTRIES);
+ }
+
+ @Test
+ public void testRead_noHeader_throws() {
+ assertThrows(IOException.class, () -> read(""));
+ }
+
+ @Test
+ public void testRead_wrongHeader_throws() {
+ assertThrows(IOException.class, () -> read("DCL2"));
+ }
+
+ @Test
+ public void testRead_oneEntry() throws Exception {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:10:loading.package:/path/fi\\\\le\n";
+ assertHasEntries(read(inputText),
+ new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+ }
+
+ @Test
+ public void testRead_emptyPackage() throws Exception {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n";
+ PackageDynamicCodeLoading info = read(inputText);
+ assertHasEntries(info, NO_ENTRIES);
+ assertHasPackages(info, NO_PACKAGES);
+ }
+
+ @Test
+ public void testRead_complex() throws Exception {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package1\n"
+ + "D:10:loading.package1,loading.package2:/path/file1\n"
+ + "D:5:loading.package1:/path/file2\n"
+ + "P:owning.package2\n"
+ + "D:0:loading.package2:/path/file3";
+ assertHasEntries(read(inputText),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+ new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+ new Entry("owning.package2", "/path/file3", 'D', 0, "loading.package2"));
+ }
+
+ @Test
+ public void testRead_missingPackageLine_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "D:10:loading.package:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_malformedFile_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "Hello world!\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_badFileType_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "X:10:loading.package:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_badUserId_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:999999999999999999:loading.package:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testRead_missingPackages_throws() {
+ String inputText = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:1:,:/path/file\n";
+ assertThrows(IOException.class, () -> read(inputText));
+ }
+
+ @Test
+ public void testWrite_empty() throws Exception {
+ assertEquals("DCL1\n", write(NO_ENTRIES));
+ }
+
+ @Test
+ public void testWrite_oneEntry() throws Exception {
+ String expected = ""
+ + "DCL1\n"
+ + "P:owning.package\n"
+ + "D:10:loading.package:/path/fi\\\\le\n";
+ String actual = write(
+ new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testWrite_complex_roundTrips() throws Exception {
+ // There isn't a canonical order for the output in the presence of multiple items.
+ // So we just check that if we read back what we write we end up where we started.
+ Entry[] entries = {
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+ new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+ new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+ new Entry("owning.package2", "/path/fi\\le3", 'D', 0, "loading.package2")
+ };
+ assertHasEntries(read(write(entries)), entries);
+ }
+
+ @Test
+ public void testWrite_failure_throws() {
+ PackageDynamicCodeLoading info = makePackageDcl(
+ new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+ assertThrows(IOException.class, () -> info.write(new ThrowingOutputStream()));
+ }
+
+ @Test
+ public void testEscape_trivialCase_returnsSameString() {
+ assertSame(TRIVIAL_STRING, escape(TRIVIAL_STRING));
+ }
+
+ @Test
+ public void testEscape() {
+ String input = "backslash\\newline\nreturn\r";
+ String expected = "backslash\\\\newline\\nreturn\\r";
+ assertEquals(expected, escape(input));
+ }
+
+ @Test
+ public void testUnescape_trivialCase_returnsSameString() throws Exception {
+ assertSame(TRIVIAL_STRING, unescape(TRIVIAL_STRING));
+ }
+
+ @Test
+ public void testUnescape() throws Exception {
+ String input = "backslash\\\\newline\\nreturn\\r";
+ String expected = "backslash\\newline\nreturn\r";
+ assertEquals(expected, unescape(input));
+ }
+
+ @Test
+ public void testUnescape_badEscape_throws() {
+ assertThrows(IOException.class, () -> unescape("this is \\bad"));
+ }
+
+ @Test
+ public void testUnescape_trailingBackslash_throws() {
+ assertThrows(IOException.class, () -> unescape("don't do this\\"));
+ }
+
+ @Test
+ public void testEscapeUnescape_roundTrips() throws Exception {
+ assertRoundTripsWithEscape("foo");
+ assertRoundTripsWithEscape("\\\\\n\n\r");
+ assertRoundTripsWithEscape("\\a\\b\\");
+ assertRoundTripsWithEscape("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
+ }
+
+ private void assertRoundTripsWithEscape(String original) throws Exception {
+ assertEquals(original, unescape(escape(original)));
+ }
+
+ private boolean record(PackageDynamicCodeLoading info, Entry entry) {
+ return info.record(entry.mOwningPackage, entry.mPath, entry.mFileType, entry.mUserId,
+ entry.mLoadingPackage);
+ }
+
+ private PackageDynamicCodeLoading read(String inputText) throws Exception {
+ ByteArrayInputStream inputStream =
+ new ByteArrayInputStream(inputText.getBytes(UTF_8));
+
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+ info.read(inputStream);
+
+ return info;
+ }
+
+ private String write(Entry... entries) throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ makePackageDcl(entries).write(output);
+ return new String(output.toByteArray(), UTF_8);
+ }
+
+ private Set<Entry> entriesFrom(PackageDynamicCodeLoading info) {
+ ImmutableSet.Builder<Entry> entries = ImmutableSet.builder();
+ for (String owningPackage : info.getAllPackagesWithDynamicCodeLoading()) {
+ PackageDynamicCode packageInfo = info.getPackageDynamicCodeInfo(owningPackage);
+ Map<String, DynamicCodeFile> usageMap = packageInfo.mFileUsageMap;
+ for (Map.Entry<String, DynamicCodeFile> fileEntry : usageMap.entrySet()) {
+ String path = fileEntry.getKey();
+ DynamicCodeFile fileInfo = fileEntry.getValue();
+ for (String loadingPackage : fileInfo.mLoadingPackages) {
+ entries.add(new Entry(owningPackage, path, fileInfo.mFileType, fileInfo.mUserId,
+ loadingPackage));
+ }
+ }
+ }
+
+ return entries.build();
+ }
+
+ private PackageDynamicCodeLoading makePackageDcl(Entry... entries) {
+ PackageDynamicCodeLoading result = new PackageDynamicCodeLoading();
+ for (Entry entry : entries) {
+ result.record(entry.mOwningPackage, entry.mPath, entry.mFileType, entry.mUserId,
+ entry.mLoadingPackage);
+ }
+ return result;
+
+ }
+
+ private void assertHasEntries(PackageDynamicCodeLoading info, Entry... expected) {
+ assertEquals(ImmutableSet.copyOf(expected), entriesFrom(info));
+ }
+
+ private void assertHasPackages(PackageDynamicCodeLoading info, String... expected) {
+ assertEquals(ImmutableSet.copyOf(expected), info.getAllPackagesWithDynamicCodeLoading());
+ }
+
+ /**
+ * Immutable representation of one entry in the dynamic code loading data (one package
+ * owning one file loaded by one package). Has well-behaved equality, hash and toString
+ * for ease of use in assertions.
+ */
+ private static class Entry {
+ private final String mOwningPackage;
+ private final String mPath;
+ private final char mFileType;
+ private final int mUserId;
+ private final String mLoadingPackage;
+
+ private Entry(String owningPackage, String path, char fileType, int userId,
+ String loadingPackage) {
+ mOwningPackage = owningPackage;
+ mPath = path;
+ mFileType = fileType;
+ mUserId = userId;
+ mLoadingPackage = loadingPackage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry that = (Entry) o;
+ return mFileType == that.mFileType
+ && mUserId == that.mUserId
+ && Objects.equals(mOwningPackage, that.mOwningPackage)
+ && Objects.equals(mPath, that.mPath)
+ && Objects.equals(mLoadingPackage, that.mLoadingPackage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOwningPackage, mPath, mFileType, mUserId, mLoadingPackage);
+ }
+
+ @Override
+ public String toString() {
+ return "Entry("
+ + "\"" + mOwningPackage + '"'
+ + ", \"" + mPath + '"'
+ + ", '" + mFileType + '\''
+ + ", " + mUserId
+ + ", \"" + mLoadingPackage + '\"'
+ + ')';
+ }
+ }
+
+ private static class ThrowingOutputStream extends OutputStream {
+ @Override
+ public void write(int b) throws IOException {
+ throw new IOException("Intentional failure");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 8ac29303268b..561c61fb979f 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -37,6 +37,7 @@ import android.os.PowerSaveState;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.app.IBatteryStats;
@@ -193,4 +194,19 @@ public class PowerManagerServiceTest extends AndroidTestCase {
assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
+
+ @MediumTest
+ public void testWasDeviceIdleFor_true() {
+ int interval = 1000;
+ mService.onUserActivity();
+ SystemClock.sleep(interval);
+ assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
+ }
+
+ @SmallTest
+ public void testWasDeviceIdleFor_false() {
+ int interval = 1000;
+ mService.onUserActivity();
+ assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index c1963da3b3af..94d293efd204 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -281,6 +281,10 @@ public class ThermalManagerServiceTest {
mFakeHal.mCallback.onValues(newSkin);
verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+ Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onValues(newBattery);
+ verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
index a9d4519104ba..70fadd101a91 100644
--- a/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
@@ -20,8 +20,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
+import android.util.ArrayMap;
+
import androidx.test.runner.AndroidJUnit4;
import com.google.common.collect.Sets;
@@ -33,6 +36,7 @@ import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Map;
import java.util.Set;
@@ -46,10 +50,25 @@ public class SignedConfigTest {
return Sets.newHashSet(values);
}
+ private static <K, V> Map<K, V> mapOf(Object... keyValuePairs) {
+ if (keyValuePairs.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ final int len = keyValuePairs.length / 2;
+ ArrayMap<K, V> m = new ArrayMap<>(len);
+ for (int i = 0; i < len; ++i) {
+ m.put((K) keyValuePairs[i * 2], (V) keyValuePairs[(i * 2) + 1]);
+ }
+ return Collections.unmodifiableMap(m);
+
+ }
+
+
@Test
public void testParsePerSdkConfigSdkMinMax() throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject("{\"minSdk\":2, \"maxSdk\": 3, \"values\": []}");
- SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, emptySet(),
+ emptyMap());
assertThat(config.minSdk).isEqualTo(2);
assertThat(config.maxSdk).isEqualTo(3);
}
@@ -58,7 +77,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigNoMinSdk() throws JSONException {
JSONObject json = new JSONObject("{\"maxSdk\": 3, \"values\": []}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -69,7 +88,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigNoMaxSdk() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"values\": []}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -80,7 +99,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigNoValues() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -88,10 +107,10 @@ public class SignedConfigTest {
}
@Test
- public void testParsePerSdkConfigSdkNullMinSdk() throws JSONException, InvalidConfigException {
+ public void testParsePerSdkConfigSdkNullMinSdk() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\":null, \"maxSdk\": 3, \"values\": []}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -99,10 +118,10 @@ public class SignedConfigTest {
}
@Test
- public void testParsePerSdkConfigSdkNullMaxSdk() throws JSONException, InvalidConfigException {
+ public void testParsePerSdkConfigSdkNullMaxSdk() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\":1, \"maxSdk\": null, \"values\": []}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -113,7 +132,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigNullValues() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": null}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -124,7 +143,8 @@ public class SignedConfigTest {
public void testParsePerSdkConfigZeroValues()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": []}");
- SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
assertThat(config.values).hasSize(0);
}
@@ -133,18 +153,30 @@ public class SignedConfigTest {
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
"{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
- SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
assertThat(config.values).containsExactly("a", "1");
}
@Test
+ public void testParsePerSdkConfigSingleKeyNullValue()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": null}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
+ assertThat(config.values.keySet()).containsExactly("a");
+ assertThat(config.values.get("a")).isNull();
+ }
+
+ @Test
public void testParsePerSdkConfigMultiKeys()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
"{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}, "
+ "{\"key\":\"c\", \"value\": \"2\"}]}");
SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(
- json, setOf("a", "b", "c"));
+ json, setOf("a", "b", "c"), emptyMap());
assertThat(config.values).containsExactly("a", "1", "c", "2");
}
@@ -153,7 +185,7 @@ public class SignedConfigTest {
JSONObject json = new JSONObject(
"{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
try {
- SignedConfig.parsePerSdkConfig(json, setOf("b"));
+ SignedConfig.parsePerSdkConfig(json, setOf("b"), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -161,11 +193,67 @@ public class SignedConfigTest {
}
@Test
+ public void testParsePerSdkConfigSingleKeyWithMap()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
+ mapOf("a", mapOf("1", "one")));
+ assertThat(config.values).containsExactly("a", "one");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMapInvalidValue() throws JSONException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"2\"}]}");
+ try {
+ SignedConfig.parsePerSdkConfig(json, setOf("b"), mapOf("a", mapOf("1", "one")));
+ fail("Expected InvalidConfigException");
+ } catch (InvalidConfigException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParsePerSdkConfigMultiKeysWithMap()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"},"
+ + "{\"key\":\"b\", \"value\": \"1\"}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ mapOf("a", mapOf("1", "one")));
+ assertThat(config.values).containsExactly("a", "one", "b", "1");
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMapToNull()
+ throws JSONException, InvalidConfigException {
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
+ mapOf("a", mapOf("1", null)));
+ assertThat(config.values).containsExactly("a", null);
+ }
+
+ @Test
+ public void testParsePerSdkConfigSingleKeyWithMapFromNull()
+ throws JSONException, InvalidConfigException {
+ assertThat(mapOf(null, "allitnil")).containsExactly(null, "allitnil");
+ assertThat(mapOf(null, "allitnil").containsKey(null)).isTrue();
+ JSONObject json = new JSONObject(
+ "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": null}]}");
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a"),
+ mapOf("a", mapOf(null, "allitnil")));
+ assertThat(config.values).containsExactly("a", "allitnil");
+ }
+
+ @Test
public void testParsePerSdkConfigSingleKeyNoValue()
throws JSONException, InvalidConfigException {
JSONObject json = new JSONObject(
"{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\"}]}");
- SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+ SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"),
+ emptyMap());
assertThat(config.values).containsExactly("a", null);
}
@@ -173,7 +261,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigValuesInvalid() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": \"foo\"}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -184,7 +272,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigConfigEntryInvalid() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [1, 2]}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -195,7 +283,7 @@ public class SignedConfigTest {
public void testParsePerSdkConfigConfigEntryNull() throws JSONException {
JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [null]}");
try {
- SignedConfig.parsePerSdkConfig(json, emptySet());
+ SignedConfig.parsePerSdkConfig(json, emptySet(), emptyMap());
fail("Expected InvalidConfigException or JSONException");
} catch (JSONException | InvalidConfigException e) {
// expected
@@ -205,14 +293,15 @@ public class SignedConfigTest {
@Test
public void testParseVersion() throws InvalidConfigException {
SignedConfig config = SignedConfig.parse(
- "{\"version\": 1, \"config\": []}", emptySet());
+ "{\"version\": 1, \"config\": []}", emptySet(), emptyMap());
assertThat(config.version).isEqualTo(1);
}
@Test
public void testParseVersionInvalid() {
try {
- SignedConfig.parse("{\"version\": \"notanint\", \"config\": []}", emptySet());
+ SignedConfig.parse("{\"version\": \"notanint\", \"config\": []}", emptySet(),
+ emptyMap());
fail("Expected InvalidConfigException");
} catch (InvalidConfigException e) {
//expected
@@ -222,7 +311,7 @@ public class SignedConfigTest {
@Test
public void testParseNoVersion() {
try {
- SignedConfig.parse("{\"config\": []}", emptySet());
+ SignedConfig.parse("{\"config\": []}", emptySet(), emptyMap());
fail("Expected InvalidConfigException");
} catch (InvalidConfigException e) {
//expected
@@ -232,7 +321,7 @@ public class SignedConfigTest {
@Test
public void testParseNoConfig() {
try {
- SignedConfig.parse("{\"version\": 1}", emptySet());
+ SignedConfig.parse("{\"version\": 1}", emptySet(), emptyMap());
fail("Expected InvalidConfigException");
} catch (InvalidConfigException e) {
//expected
@@ -242,7 +331,7 @@ public class SignedConfigTest {
@Test
public void testParseConfigNull() {
try {
- SignedConfig.parse("{\"version\": 1, \"config\": null}", emptySet());
+ SignedConfig.parse("{\"version\": 1, \"config\": null}", emptySet(), emptyMap());
fail("Expected InvalidConfigException");
} catch (InvalidConfigException e) {
//expected
@@ -252,7 +341,7 @@ public class SignedConfigTest {
@Test
public void testParseVersionNull() {
try {
- SignedConfig.parse("{\"version\": null, \"config\": []}", emptySet());
+ SignedConfig.parse("{\"version\": null, \"config\": []}", emptySet(), emptyMap());
fail("Expected InvalidConfigException");
} catch (InvalidConfigException e) {
//expected
@@ -262,7 +351,7 @@ public class SignedConfigTest {
@Test
public void testParseConfigInvalidEntry() {
try {
- SignedConfig.parse("{\"version\": 1, \"config\": [{}]}", emptySet());
+ SignedConfig.parse("{\"version\": 1, \"config\": [{}]}", emptySet(), emptyMap());
fail("Expected InvalidConfigException");
} catch (InvalidConfigException e) {
//expected
@@ -273,7 +362,7 @@ public class SignedConfigTest {
public void testParseSdkConfigSingle() throws InvalidConfigException {
SignedConfig config = SignedConfig.parse(
"{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}]}",
- emptySet());
+ emptySet(), emptyMap());
assertThat(config.perSdkConfig).hasSize(1);
}
@@ -281,7 +370,8 @@ public class SignedConfigTest {
public void testParseSdkConfigMultiple() throws InvalidConfigException {
SignedConfig config = SignedConfig.parse(
"{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}, "
- + "{\"minSdk\": 2, \"maxSdk\": 2, \"values\": []}]}", emptySet());
+ + "{\"minSdk\": 2, \"maxSdk\": 2, \"values\": []}]}", emptySet(),
+ emptyMap());
assertThat(config.perSdkConfig).hasSize(2);
}
@@ -314,7 +404,6 @@ public class SignedConfigTest {
SignedConfig config = new SignedConfig(0, Arrays.asList(sdk13, sdk46));
assertThat(config.getMatchingConfig(2)).isEqualTo(sdk13);
}
-
@Test
public void testGetMatchingConfigNoMatch() {
SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
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 d950360791d2..527a1eef7b0f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREG
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
@@ -183,7 +184,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Resources mResources;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
@Mock
private NotificationListeners mListeners;
@Mock private NotificationAssistants mAssistants;
@@ -238,6 +239,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
protected void reportUserInteraction(NotificationRecord r) {
return;
}
+
+ @Override
+ protected void handleSavePolicyFile() {
+ return;
+ }
}
private class TestableToastCallback extends ITransientNotification.Stub {
@@ -425,7 +431,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
final NotificationChannel createdChannel =
@@ -447,9 +453,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_TwoChannels() throws Exception {
final NotificationChannel channel1 =
- new NotificationChannel("id1", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id1", "name", IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
- new NotificationChannel("id2", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id2", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
assertTrue(mBinderService.getNotificationChannel(PKG, "id1") != null);
@@ -460,7 +466,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateNotificationChannels_SecondCreateDoesNotChangeImportance()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -471,14 +477,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
new ParceledListSlice(Arrays.asList(dupeChannel)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
public void testCreateNotificationChannels_SecondCreateAllowedToDowngradeImportance()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -496,7 +502,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateNotificationChannels_CannotDowngradeImportanceIfAlreadyUpdated()
throws Exception {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel)));
@@ -519,14 +525,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateNotificationChannels_IdenticalChannelsInListIgnoresSecond()
throws Exception {
final NotificationChannel channel1 =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
@@ -751,13 +757,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testBlockedNotifications_blockedByAssistant() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
mService.addEnqueuedNotification(r);
- r.setAssistantImportance(IMPORTANCE_NONE);
+ Bundle bundle = new Bundle();
+ bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(r.getKey());
@@ -1809,7 +1820,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
-
try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
@@ -2613,7 +2623,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testAssistantIBlockingTriggersCancel() throws Exception {
+ public void testAssistantBlockingTriggersCancel() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
NotificationManagerService.WorkerHandler handler = mock(
@@ -2653,6 +2663,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testApplyEnqueuedAdjustmentFromAssistant_importance_onTime() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(IMPORTANCE_LOW, r.getImportance());
+ }
+
+ @Test
+ public void testApplyEnqueuedAdjustmentFromAssistant_importance_tooLate() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(IMPORTANCE_DEFAULT, r.getImportance());
+ assertFalse(r.hasAdjustment(Adjustment.KEY_IMPORTANCE));
+ }
+
+ @Test
public void testApplyEnqueuedAdjustmentFromAssistant_crossUser() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addEnqueuedNotification(r);
@@ -2852,7 +2899,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void updateUriPermissions_update() throws Exception {
NotificationChannel c = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
Message message1 = new Message("", 0, "");
message1.setData("",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 8690110ca66a..0eeeeed0b227 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -760,6 +760,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(IMPORTANCE_LOW, record.getImportance());
// assistant ignored if user expressed preference
@@ -767,6 +768,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
channel.lockFields(USER_LOCKED_IMPORTANCE);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(channel.getImportance(), record.getImportance());
}
@@ -779,6 +781,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
record.setAssistantImportance(IMPORTANCE_LOW);
+ record.calculateImportance();
assertEquals(IMPORTANCE_LOW, record.getImportance());
record.updateNotificationChannel(
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 613ba0044f6a..904d55e6d0f3 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -32,7 +32,9 @@ import android.service.usb.UsbConnectionRecordProto;
import android.service.usb.UsbHostManagerProto;
import android.service.usb.UsbIsHeadsetProto;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -86,6 +88,7 @@ public class UsbHostManager {
private int mNumConnects; // TOTAL # of connect/disconnect
private final LinkedList<ConnectionRecord> mConnections = new LinkedList<ConnectionRecord>();
private ConnectionRecord mLastConnect;
+ private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
/*
* ConnectionRecord
@@ -300,6 +303,11 @@ public class UsbHostManager {
if (mode != ConnectionRecord.DISCONNECT) {
mLastConnect = rec;
}
+ if (mode == ConnectionRecord.CONNECT) {
+ mConnected.put(deviceAddress, rec);
+ } else if (mode == ConnectionRecord.DISCONNECT) {
+ mConnected.remove(deviceAddress);
+ }
}
private void logUsbDevice(UsbDescriptorParser descriptorParser) {
@@ -408,6 +416,14 @@ public class UsbHostManager {
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
parser.getRawDescriptors());
+
+ // Stats collection
+ if (parser.hasAudioInterface()) {
+ StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
+ newDevice.getProductId(), parser.hasAudioInterface(),
+ parser.hasHIDInterface(), parser.hasStorageInterface(),
+ StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
+ }
}
}
@@ -432,9 +448,22 @@ public class UsbHostManager {
mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
-
+ ConnectionRecord current = mConnected.get(deviceAddress);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
+
+ if (current != null) {
+ UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
+ current.mDescriptors);
+ if (parser.hasAudioInterface()) {
+ // Stats collection
+ StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
+ device.getProductId(), parser.hasAudioInterface(),
+ parser.hasHIDInterface(), parser.hasStorageInterface(),
+ StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
+ System.currentTimeMillis() - current.mTimestamp);
+ }
+ }
} else {
Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 33da4033174a..96618f569928 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -41,12 +41,14 @@ import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.usb.UsbPortInfoProto;
import android.service.usb.UsbPortManagerProto;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -54,6 +56,7 @@ import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.NoSuchElementException;
/**
@@ -117,6 +120,10 @@ public class UsbPortManager {
private final ArrayMap<String, RawPortInfo> mSimulatedPorts =
new ArrayMap<>();
+ // Maintains the current connected status of the port.
+ // Uploads logs only when the connection status is changes.
+ private final HashMap<String, Boolean> mConnected = new HashMap<>();
+
public UsbPortManager(Context context) {
mContext = context;
try {
@@ -700,6 +707,18 @@ public class UsbPortManager {
// Guard against possible reentrance by posting the broadcast from the handler
// instead of from within the critical section.
mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+
+ // Log to statsd
+ if (!mConnected.containsKey(portInfo.mUsbPort.getId())
+ || (mConnected.get(portInfo.mUsbPort.getId())
+ != portInfo.mUsbPortStatus.isConnected())) {
+ mConnected.put(portInfo.mUsbPort.getId(), portInfo.mUsbPortStatus.isConnected());
+ StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED,
+ portInfo.mUsbPortStatus.isConnected()
+ ? StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_CONNECTED :
+ StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
+ portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis);
+ }
}
private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
@@ -746,7 +765,12 @@ public class UsbPortManager {
public boolean mCanChangeMode;
public boolean mCanChangePowerRole;
public boolean mCanChangeDataRole;
- public int mDisposition; // default initialized to 0 which means added
+ // default initialized to 0 which means added
+ public int mDisposition;
+ // Tracks elapsedRealtime() of when the port was connected
+ public long mConnectedAtMillis;
+ // 0 when port is connected. Else reports the last connected duration
+ public long mLastConnectDurationMillis;
public PortInfo(String portId, int supportedModes) {
mUsbPort = new UsbPort(portId, supportedModes);
@@ -756,6 +780,8 @@ public class UsbPortManager {
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
int supportedRoleCombinations) {
+ boolean dispositionChanged = false;
+
mCanChangeMode = canChangeMode;
mCanChangePowerRole = canChangePowerRole;
mCanChangeDataRole = canChangeDataRole;
@@ -767,9 +793,18 @@ public class UsbPortManager {
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations);
- return true;
+ dispositionChanged = true;
}
- return false;
+
+ if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) {
+ mConnectedAtMillis = SystemClock.elapsedRealtime();
+ mLastConnectDurationMillis = 0;
+ } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) {
+ mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis;
+ mConnectedAtMillis = 0;
+ }
+
+ return dispositionChanged;
}
void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
@@ -782,6 +817,10 @@ public class UsbPortManager {
mCanChangePowerRole);
dump.write("can_change_data_role", UsbPortInfoProto.CAN_CHANGE_DATA_ROLE,
mCanChangeDataRole);
+ dump.write("connected_at_millis",
+ UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
+ dump.write("last_connect_duration_millis",
+ UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
dump.end(token);
}
@@ -791,7 +830,9 @@ public class UsbPortManager {
return "port=" + mUsbPort + ", status=" + mUsbPortStatus
+ ", canChangeMode=" + mCanChangeMode
+ ", canChangePowerRole=" + mCanChangePowerRole
- + ", canChangeDataRole=" + mCanChangeDataRole;
+ + ", canChangeDataRole=" + mCanChangeDataRole
+ + ", connectedAtMillis=" + mConnectedAtMillis
+ + ", lastConnectDurationMillis=" + mLastConnectDurationMillis;
}
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d617de0af6a1..36d0188048c3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -122,10 +122,21 @@ public final class Call {
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
+ * @deprecated Use the list from {@link #EXTRA_SUGGESTED_PHONE_ACCOUNTS} instead.
*/
+ @Deprecated
public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
/**
+ * Key for extra used to pass along a list of {@link PhoneAccountSuggestion}s to the in-call
+ * UI when a call enters the {@link #STATE_SELECT_PHONE_ACCOUNT} state. The list included here
+ * will have the same length and be in the same order as the list passed with
+ * {@link #AVAILABLE_PHONE_ACCOUNTS}.
+ */
+ public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS =
+ "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
+
+ /**
* Extra key used to indicate the time (in milliseconds since midnight, January 1, 1970 UTC)
* when the last outgoing emergency call was made. This is used to identify potential emergency
* callbacks.
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
new file mode 100644
index 000000000000..4e6a178c8170
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public final class PhoneAccountSuggestion implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {REASON_NONE, REASON_INTRA_CARRIER, REASON_FREQUENT,
+ REASON_USER_SET, REASON_OTHER}, prefix = { "REASON_" })
+ public @interface SuggestionReason {}
+
+ /**
+ * Indicates that this account is not suggested for use, but is still available.
+ */
+ public static final int REASON_NONE = 0;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested because the number we're calling
+ * is on the same carrier, and therefore may have lower rates.
+ */
+ public static final int REASON_INTRA_CARRIER = 1;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested because the user uses it
+ * frequently for the number that we are calling.
+ */
+ public static final int REASON_FREQUENT = 2;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested because the user explicitly
+ * specified that it be used for the number we are calling.
+ */
+ public static final int REASON_USER_SET = 3;
+
+ /**
+ * Indicates that the {@link PhoneAccountHandle} is suggested for a reason not otherwise
+ * enumerated here.
+ */
+ public static final int REASON_OTHER = 4;
+
+ private PhoneAccountHandle mHandle;
+ private int mReason;
+ private boolean mShouldAutoSelect;
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public PhoneAccountSuggestion(PhoneAccountHandle handle, @SuggestionReason int reason,
+ boolean shouldAutoSelect) {
+ this.mHandle = handle;
+ this.mReason = reason;
+ this.mShouldAutoSelect = shouldAutoSelect;
+ }
+
+ private PhoneAccountSuggestion(Parcel in) {
+ mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader());
+ mReason = in.readInt();
+ mShouldAutoSelect = in.readByte() != 0;
+ }
+
+ public static final Creator<PhoneAccountSuggestion> CREATOR =
+ new Creator<PhoneAccountSuggestion>() {
+ @Override
+ public PhoneAccountSuggestion createFromParcel(Parcel in) {
+ return new PhoneAccountSuggestion(in);
+ }
+
+ @Override
+ public PhoneAccountSuggestion[] newArray(int size) {
+ return new PhoneAccountSuggestion[size];
+ }
+ };
+
+ /**
+ * @return The {@link PhoneAccountHandle} for this suggestion.
+ */
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mHandle;
+ }
+
+ /**
+ * @return The reason for this suggestion
+ */
+ public @SuggestionReason int getReason() {
+ return mReason;
+ }
+
+ /**
+ * Suggests whether the dialer should automatically place the call using this account without
+ * user interaction. This may be set on multiple {@link PhoneAccountSuggestion}s, and the dialer
+ * is free to choose which one to use.
+ * @return {@code true} if the hint is to auto-select, {@code false} otherwise.
+ */
+ public boolean shouldAutoSelect() {
+ return mShouldAutoSelect;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mHandle, flags);
+ dest.writeInt(mReason);
+ dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0));
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 143b323892f3..d64efea5fa63 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -305,6 +305,8 @@ interface ITelecomService {
*/
void handleCallIntent(in Intent intent);
+ void setTestDefaultCallRedirectionApp(String packageName);
+
void setTestDefaultCallScreeningApp(String packageName);
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0a60e3409c9d..60eb18cacb88 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -78,6 +78,15 @@ public class CarrierConfigManager {
// system image, that can be added in packages/apps/CarrierConfig.
/**
+ * Specifies a value that identifies the version of the carrier configuration that is
+ * currently in use. This string is displayed on the UI.
+ * The format of the string is not specified.
+ * @hide
+ */
+ public static final String KEY_CARRIER_CONFIG_VERSION_STRING =
+ "carrier_config_version_string";
+
+ /**
* This flag specifies whether VoLTE availability is based on provisioning. By default this is
* false.
*/
@@ -2392,6 +2401,7 @@ public class CarrierConfigManager {
static {
sDefaults = new PersistableBundle();
+ sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index f6e8d3422eca..c95837e1e1de 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1738,7 +1738,10 @@ public class PhoneNumberUtils {
* @param number the number to look up.
* @return true if the number is in the list of emergency numbers
* listed in the RIL / SIM, otherwise return false.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} instead.
*/
+ @Deprecated
public static boolean isEmergencyNumber(String number) {
return isEmergencyNumber(getDefaultVoiceSubId(), number);
}
@@ -1751,8 +1754,13 @@ public class PhoneNumberUtils {
* @param number the number to look up.
* @return true if the number is in the list of emergency numbers
* listed in the RIL / SIM, otherwise return false.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isEmergencyNumber(int subId, String number) {
// Return true only if the specified number *exactly* matches
@@ -1778,8 +1786,12 @@ public class PhoneNumberUtils {
* listed in the RIL / SIM, *or* if the number starts with the
* same digits as any of those emergency numbers.
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isPotentialEmergencyNumber(String number) {
return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
}
@@ -1802,9 +1814,14 @@ public class PhoneNumberUtils {
* @return true if the number is in the list of emergency numbers
* listed in the RIL / SIM, *or* if the number starts with the
* same digits as any of those emergency numbers.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
@UnsupportedAppUsage
+ @Deprecated
public static boolean isPotentialEmergencyNumber(int subId, String number) {
// Check against the emergency numbers listed by the RIL / SIM,
// and *don't* require an exact match.
@@ -1867,8 +1884,12 @@ public class PhoneNumberUtils {
* @return if the number is an emergency number for the specific country, then return true,
* otherwise false
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
@@ -1882,8 +1903,13 @@ public class PhoneNumberUtils {
* @param defaultCountryIso the specific country which the number should be checked against
* @return if the number is an emergency number for the specific country, then return true,
* otherwise false
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
return isEmergencyNumberInternal(subId, number,
defaultCountryIso,
@@ -1909,8 +1935,12 @@ public class PhoneNumberUtils {
* country, *or* if the number starts with the same digits as
* any of those emergency numbers.
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
}
@@ -1934,8 +1964,13 @@ public class PhoneNumberUtils {
* @return true if the number is an emergency number for the specific
* country, *or* if the number starts with the same digits as
* any of those emergency numbers.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
public static boolean isPotentialEmergencyNumber(int subId, String number,
String defaultCountryIso) {
return isEmergencyNumberInternal(subId, number,
@@ -1983,92 +2018,7 @@ public class PhoneNumberUtils {
private static boolean isEmergencyNumberInternal(int subId, String number,
String defaultCountryIso,
boolean useExactMatch) {
- // If the number passed in is null, just return false:
- if (number == null) return false;
-
- // If the number passed in is a SIP address, return false, since the
- // concept of "emergency numbers" is only meaningful for calls placed
- // over the cell network.
- // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
- // since the whole point of extractNetworkPortionAlt() is to filter out
- // any non-dialable characters (which would turn 'abc911def@example.com'
- // into '911', for example.))
- if (isUriNumber(number)) {
- return false;
- }
-
- // Strip the separators from the number before comparing it
- // to the list.
- number = extractNetworkPortionAlt(number);
-
- String emergencyNumbers = "";
- int slotId = SubscriptionManager.getSlotIndex(subId);
-
- // retrieve the list of emergency numbers
- // check read-write ecclist property first
- String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-
- emergencyNumbers = SystemProperties.get(ecclist, "");
-
- Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
- + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers);
-
- if (TextUtils.isEmpty(emergencyNumbers)) {
- // then read-only ecclist property since old RIL only uses this
- emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
- }
-
- if (!TextUtils.isEmpty(emergencyNumbers)) {
- // searches through the comma-separated list for a match,
- // return true if one is found.
- for (String emergencyNum : emergencyNumbers.split(",")) {
- // It is not possible to append additional digits to an emergency number to dial
- // the number in Brazil - it won't connect.
- if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
- if (number.equals(emergencyNum)) {
- return true;
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- }
- }
- }
- // no matches found against the list!
- return false;
- }
-
- Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
- + " Use embedded logic for determining ones.");
-
- // If slot id is invalid, means that there is no sim card.
- // According spec 3GPP TS22.101, the following numbers should be
- // ECC numbers when SIM/USIM is not present.
- emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
-
- for (String emergencyNum : emergencyNumbers.split(",")) {
- if (useExactMatch) {
- if (number.equals(emergencyNum)) {
- return true;
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- }
- }
- }
-
- // No ecclist system property, so use our own list.
- if (defaultCountryIso != null) {
- ShortNumberInfo info = ShortNumberInfo.getInstance();
- if (useExactMatch) {
- return info.isEmergencyNumber(number, defaultCountryIso);
- } else {
- return info.connectsToEmergencyNumber(number, defaultCountryIso);
- }
- }
-
- return false;
+ return TelephonyManager.getDefault().isCurrentEmergencyNumber(number);
}
/**
@@ -2078,7 +2028,11 @@ public class PhoneNumberUtils {
* @param context the specific context which the number should be checked against
* @return true if the specified number is an emergency number for the country the user
* is currently in.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
*/
+ @Deprecated
public static boolean isLocalEmergencyNumber(Context context, String number) {
return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
}
@@ -2091,8 +2045,13 @@ public class PhoneNumberUtils {
* @param context the specific context which the number should be checked against
* @return true if the specified number is an emergency number for the country the user
* is currently in.
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
return isLocalEmergencyNumberInternal(subId, number,
@@ -2120,8 +2079,13 @@ public class PhoneNumberUtils {
* CountryDetector.
*
* @see android.location.CountryDetector
+ *
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
@@ -2147,9 +2111,13 @@ public class PhoneNumberUtils {
* @return true if the specified number is an emergency number for a local country, based on the
* CountryDetector.
*
+ * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+ * instead.
+ *
* @hide
*/
@UnsupportedAppUsage
+ @Deprecated
public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
String number) {
return isLocalEmergencyNumberInternal(subId, number,
@@ -2217,6 +2185,101 @@ public class PhoneNumberUtils {
}
/**
+ * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose.
+ *
+ * @hide
+ */
+ public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch,
+ String defaultCountryIso) {
+ // If the number passed in is null, just return false:
+ if (number == null) return false;
+
+ // If the number passed in is a SIP address, return false, since the
+ // concept of "emergency numbers" is only meaningful for calls placed
+ // over the cell network.
+ // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
+ // since the whole point of extractNetworkPortionAlt() is to filter out
+ // any non-dialable characters (which would turn 'abc911def@example.com'
+ // into '911', for example.))
+ if (PhoneNumberUtils.isUriNumber(number)) {
+ return false;
+ }
+
+ // Strip the separators from the number before comparing it
+ // to the list.
+ number = PhoneNumberUtils.extractNetworkPortionAlt(number);
+
+ String emergencyNumbers = "";
+ int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId());
+
+ // retrieve the list of emergency numbers
+ // check read-write ecclist property first
+ String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
+
+ emergencyNumbers = SystemProperties.get(ecclist, "");
+
+ Rlog.d(LOG_TAG, "slotId:" + slotId + " country:"
+ + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers);
+
+ if (TextUtils.isEmpty(emergencyNumbers)) {
+ // then read-only ecclist property since old RIL only uses this
+ emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+ }
+
+ if (!TextUtils.isEmpty(emergencyNumbers)) {
+ // searches through the comma-separated list for a match,
+ // return true if one is found.
+ for (String emergencyNum : emergencyNumbers.split(",")) {
+ // It is not possible to append additional digits to an emergency number to dial
+ // the number in Brazil - it won't connect.
+ if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
+ }
+ }
+ // no matches found against the list!
+ return false;
+ }
+
+ Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
+ + " Use embedded logic for determining ones.");
+
+ // If slot id is invalid, means that there is no sim card.
+ // According spec 3GPP TS22.101, the following numbers should be
+ // ECC numbers when SIM/USIM is not present.
+ emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
+
+ for (String emergencyNum : emergencyNumbers.split(",")) {
+ if (useExactMatch) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
+ }
+ }
+
+ // No ecclist system property, so use our own list.
+ if (defaultCountryIso != null) {
+ ShortNumberInfo info = ShortNumberInfo.getInstance();
+ if (useExactMatch) {
+ return info.isEmergencyNumber(number, defaultCountryIso);
+ } else {
+ return info.connectsToEmergencyNumber(number, defaultCountryIso);
+ }
+ }
+
+ return false;
+ }
+
+ /**
* isVoiceMailNumber: checks a given number against the voicemail
* number provided by the RIL and SIM card. The caller must have
* the READ_PHONE_STATE credential.
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e8a28cac3140..0df0dafbe1dd 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -27,12 +27,14 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
+import android.telephony.emergency.EmergencyNumber;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -313,6 +315,8 @@ public class PhoneStateListener {
*
* <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
* app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see #onEmergencyNumberListChanged
*/
public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
@@ -603,6 +607,21 @@ public class PhoneStateListener {
}
/**
+ * Callback invoked when the current emergency number list has changed
+ *
+ * @param emergencyNumberList Map including the key as the active subscription ID
+ * (Note: if there is no active subscription, the key is
+ * {@link SubscriptionManager#getDefaultSubscriptionId})
+ * and the value as the list of {@link EmergencyNumber};
+ * null if this information is not available.
+ * @hide
+ */
+ public void onEmergencyNumberListChanged(
+ @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when OEM hook raw event is received. Requires
* the READ_PRIVILEGED_PHONE_STATE permission.
* @param rawData is the byte array of the OEM hook raw data.
@@ -859,6 +878,16 @@ public class PhoneStateListener {
() -> psl.onPhysicalChannelConfigurationChanged(configs)));
}
+ @Override
+ public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
+ }
+
public void onPhoneCapabilityChanged(PhoneCapability capability) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 7c52d38b37b0..78f05168198f 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1423,47 +1423,49 @@ public class ServiceState implements Parcelable {
}
/** @hide */
- public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rt) {
- switch(rt) {
- case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
- return TelephonyManager.NETWORK_TYPE_GPRS;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
- return TelephonyManager.NETWORK_TYPE_EDGE;
- case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
- return TelephonyManager.NETWORK_TYPE_UMTS;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
- return TelephonyManager.NETWORK_TYPE_HSDPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
- return TelephonyManager.NETWORK_TYPE_HSUPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
- return TelephonyManager.NETWORK_TYPE_HSPA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
- case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
- return TelephonyManager.NETWORK_TYPE_CDMA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
- return TelephonyManager.NETWORK_TYPE_1xRTT;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
- return TelephonyManager.NETWORK_TYPE_EVDO_0;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
- return TelephonyManager.NETWORK_TYPE_EVDO_A;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
- return TelephonyManager.NETWORK_TYPE_EVDO_B;
- case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
- return TelephonyManager.NETWORK_TYPE_EHRPD;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
- return TelephonyManager.NETWORK_TYPE_LTE;
- case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
- return TelephonyManager.NETWORK_TYPE_HSPAP;
- case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
- return TelephonyManager.NETWORK_TYPE_GSM;
- case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
- return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
- case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
- return TelephonyManager.NETWORK_TYPE_IWLAN;
- case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
- return TelephonyManager.NETWORK_TYPE_LTE_CA;
- default:
- return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) {
+ switch(rat) {
+ case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+ return TelephonyManager.NETWORK_TYPE_GPRS;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+ return TelephonyManager.NETWORK_TYPE_EDGE;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+ return TelephonyManager.NETWORK_TYPE_UMTS;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+ return TelephonyManager.NETWORK_TYPE_HSDPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+ return TelephonyManager.NETWORK_TYPE_HSUPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+ return TelephonyManager.NETWORK_TYPE_HSPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+ return TelephonyManager.NETWORK_TYPE_CDMA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+ return TelephonyManager.NETWORK_TYPE_1xRTT;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+ return TelephonyManager.NETWORK_TYPE_EVDO_0;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+ return TelephonyManager.NETWORK_TYPE_EVDO_A;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+ return TelephonyManager.NETWORK_TYPE_EVDO_B;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+ return TelephonyManager.NETWORK_TYPE_EHRPD;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+ return TelephonyManager.NETWORK_TYPE_HSPAP;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
+ return TelephonyManager.NETWORK_TYPE_GSM;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return TelephonyManager.NETWORK_TYPE_LTE_CA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
+ return TelephonyManager.NETWORK_TYPE_NR;
+ default:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2c06c4720650..aa2a69d6f6b3 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -19,6 +19,7 @@ package android.telephony;
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
@@ -52,6 +53,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -67,6 +69,7 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* SubscriptionManager is the application interface to SubscriptionController
@@ -2405,16 +2408,21 @@ public class SubscriptionManager {
* together, some of them may be invisible to the users, etc.
*
* Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or can manage all subscriptions in the list, according to their
- * acess rules.
+ * permission or had carrier privilege permission on the subscriptions:
+ * {@link TelephonyManager#hasCarrierPrivileges(int)} or
+ * {@link #canManageSubscription(SubscriptionInfo)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*
* @param subIdList list of subId that will be in the same group
* @return groupUUID a UUID assigned to the subscription group. It returns
* null if fails.
*
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public String setSubscriptionGroup(int[] subIdList) {
+ public @Nullable String setSubscriptionGroup(@NonNull int[] subIdList) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (VDBG) {
logd("[setSubscriptionGroup]+ subIdList:" + Arrays.toString(subIdList));
@@ -2434,6 +2442,80 @@ public class SubscriptionManager {
}
/**
+ * Remove a list of subscriptions from their subscription group.
+ * See {@link #setSubscriptionGroup(int[])} for more details.
+ *
+ * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscriptions:
+ * {@link TelephonyManager#hasCarrierPrivileges(int)} or
+ * {@link #canManageSubscription(SubscriptionInfo)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ *
+ * @param subIdList list of subId that need removing from their groups.
+ * @return whether the operation succeeds.
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean removeSubscriptionsFromGroup(@NonNull int[] subIdList) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (VDBG) {
+ logd("[removeSubscriptionsFromGroup]+ subIdList:" + Arrays.toString(subIdList));
+ }
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.removeSubscriptionsFromGroup(subIdList, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
+ * See {@link #setSubscriptionGroup(int[])} for more details.
+ *
+ * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
+ * permission or had carrier privilege permission on the subscription.
+ * {@link TelephonyManager#hasCarrierPrivileges(int)}
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ *
+ * @param subId of which list of subInfo from the same group will be returned.
+ * @return list of subscriptionInfo that belong to the same group, including the given
+ * subscription itself. It will return null if the subscription doesn't exist or it
+ * doesn't belong to any group.
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public @Nullable List<SubscriptionInfo> getSubscriptionsInGroup(int subId) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (VDBG) {
+ logd("[getSubscriptionsInGroup]+ subId:" + subId);
+ }
+
+ List<SubscriptionInfo> result = null;
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getSubscriptionsInGroup(subId, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
* Set metered by simInfo index
*
* @param isMetered whether it’s a metered subscription.
@@ -2448,6 +2530,42 @@ public class SubscriptionManager {
(iSub)-> iSub.setMetered(isMetered, subId));
}
+ /**
+ * Whether system UI should hide a subscription. If it's a bundled opportunistic
+ * subscription, it shouldn't show up in anywhere in Settings app, dialer app,
+ * or status bar.
+ *
+ * @param info the subscriptionInfo to check against.
+ * @return true if this subscription should be hidden.
+ *
+ * @hide
+ */
+ public static boolean shouldHideSubscription(SubscriptionInfo info) {
+ return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+ }
+
+ /**
+ * Return a list of subscriptions that are available and visible to the user.
+ * Used by Settings app to show a list of subscriptions for user to pick.
+ *
+ * <p>
+ * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+ * for getSelectableSubscriptionInfoList to be invoked.
+ * @return list of user selectable subscriptions.
+ *
+ * @hide
+ */
+ public @Nullable List<SubscriptionInfo> getSelectableSubscriptionInfoList() {
+ List<SubscriptionInfo> availableList = getAvailableSubscriptionInfoList();
+ if (availableList == null) {
+ return null;
+ } else {
+ return getAvailableSubscriptionInfoList().stream()
+ .filter(subInfo -> !shouldHideSubscription(subInfo))
+ .collect(Collectors.toList());
+ }
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fa9b76de2e6b..33f856726791 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -810,6 +810,7 @@ public class TelephonyManager {
* @see TelephonyManager#NETWORK_TYPE_LTE
* @see TelephonyManager#NETWORK_TYPE_EHRPD
* @see TelephonyManager#NETWORK_TYPE_HSPAP
+ * @see TelephonyManager#NETWORK_TYPE_NR
*
* <p class="note">
* Retrieve with
@@ -2328,6 +2329,7 @@ public class TelephonyManager {
* @see #NETWORK_TYPE_LTE
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
+ * @see #NETWORK_TYPE_NR
*
* @hide
*/
@@ -2379,6 +2381,7 @@ public class TelephonyManager {
* @see #NETWORK_TYPE_LTE
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
+ * @see #NETWORK_TYPE_NR
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -2565,6 +2568,8 @@ public class TelephonyManager {
return "IWLAN";
case NETWORK_TYPE_LTE_CA:
return "LTE_CA";
+ case NETWORK_TYPE_NR:
+ return "NR";
default:
return "UNKNOWN";
}
@@ -6627,7 +6632,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCarrierPrivilegeStatus(mSubId) ==
+ return telephony.getCarrierPrivilegeStatus(subId) ==
CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
} catch (RemoteException ex) {
@@ -9449,8 +9454,13 @@ public class TelephonyManager {
/**
* Get the emergency number list based on current locale, sim, default, modem and network.
*
- * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
- * the smaller index in the returned list.
+ * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+ * priority sources will be located at the smaller index; the priority order of sources are:
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
*
* <p>The subscriptions which the returned list would be based on, are all the active
* subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9459,8 +9469,9 @@ public class TelephonyManager {
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return Map including the key as the active subscription ID (Note: if there is no active
- * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
- * as the list of {@link EmergencyNumber}; null if this information is not available.
+ * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+ * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+ * a SecurityException if the caller does not have the permission.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@Nullable
@@ -9481,8 +9492,13 @@ public class TelephonyManager {
* Get the per-category emergency number list based on current locale, sim, default, modem
* and network.
*
- * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
- * the smaller index in the returned list.
+ * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+ * priority sources will be located at the smaller index; the priority order of sources are:
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+ * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
*
* <p>The subscriptions which the returned list would be based on, are all the active
* subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9503,8 +9519,9 @@ public class TelephonyManager {
* <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
* </ol>
* @return Map including the key as the active subscription ID (Note: if there is no active
- * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
- * as the list of {@link EmergencyNumber}; null if this information is not available.
+ * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+ * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+ * a SecurityException if the caller does not have the permission.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@Nullable
@@ -9551,7 +9568,44 @@ public class TelephonyManager {
if (telephony == null) {
return false;
}
- return telephony.isCurrentEmergencyNumber(number);
+ return telephony.isCurrentEmergencyNumber(number, true);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the supplied number is an emergency number based on current locale, sim, default,
+ * modem and network.
+ *
+ * <p> Specifically, this method will return {@code true} if the specified number is an
+ * emergency number, *or* if the number simply starts with the same digits as any current
+ * emergency number.
+ *
+ * <p>The subscriptions which the identification would be based on, are all the active
+ * subscriptions, no matter which subscription could be used to create TelephonyManager.
+ *
+ * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+ * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param number - the number to look up
+ * @return {@code true} if the given number is an emergency number or it simply starts with
+ * the same digits of any current emergency number based on current locale, sim, modem and
+ * network; {@code false} if it is not; or throw an SecurityException if the caller does not
+ * have the required permission/privileges
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isCurrentPotentialEmergencyNumber(@NonNull String number) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return false;
+ }
+ return telephony.isCurrentEmergencyNumber(number, false);
} catch (RemoteException ex) {
Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a1afc0892ba0..8d148c36f3e7 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -969,7 +969,7 @@ public class ApnSetting implements Parcelable {
*/
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("[ApnSettingV5] ")
+ sb.append("[ApnSettingV6] ")
.append(mEntryName)
.append(", ").append(mId)
.append(", ").append(mOperatorNumeric)
@@ -996,6 +996,7 @@ public class ApnSetting implements Parcelable {
sb.append(", ").append(mPermanentFailed);
sb.append(", ").append(mNetworkTypeBitmask);
sb.append(", ").append(mApnSetId);
+ sb.append(", ").append(mCarrierId);
return sb.toString();
}
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 41f7bd7ade63..fe062d5d974a 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -22,6 +22,7 @@ import android.hardware.radio.V1_4.EmergencyNumberSource;
import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -150,6 +151,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
@IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
EMERGENCY_NUMBER_SOURCE_SIM,
+ EMERGENCY_NUMBER_SOURCE_DATABASE,
EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
EMERGENCY_NUMBER_SOURCE_DEFAULT
})
@@ -169,6 +171,10 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
* Reference: 3gpp 22.101, Section 10 - Emergency Calls
*/
public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+ /**
+ * Bit-field which indicates the number is from the platform-maintained database.
+ */
+ public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 1 << 4;
/** Bit-field which indicates the number is from the modem config. */
public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
EmergencyNumberSource.MODEM_CONFIG;
@@ -187,21 +193,24 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+ EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
}
private final String mNumber;
private final String mCountryIso;
+ private final String mMnc;
private final int mEmergencyServiceCategoryBitmask;
private final int mEmergencyNumberSourceBitmask;
/** @hide */
public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
- int emergencyServiceCategories,
+ @NonNull String mnc, int emergencyServiceCategories,
int emergencyNumberSources) {
this.mNumber = number;
this.mCountryIso = countryIso;
+ this.mMnc = mnc;
this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
}
@@ -210,6 +219,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
public EmergencyNumber(Parcel source) {
mNumber = source.readString();
mCountryIso = source.readString();
+ mMnc = source.readString();
mEmergencyServiceCategoryBitmask = source.readInt();
mEmergencyNumberSourceBitmask = source.readInt();
}
@@ -236,6 +246,15 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
}
/**
+ * Get the Mobile Network Code of the emergency number.
+ *
+ * @return the Mobile Network Code of the emergency number.
+ */
+ public String getMnc() {
+ return mMnc;
+ }
+
+ /**
* Returns the bitmask of emergency service categories of the emergency number.
*
* @return bitmask of the emergency service categories
@@ -338,6 +357,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mNumber);
dest.writeString(mCountryIso);
+ dest.writeString(mMnc);
dest.writeInt(mEmergencyServiceCategoryBitmask);
dest.writeInt(mEmergencyNumberSourceBitmask);
}
@@ -350,10 +370,10 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
@Override
public String toString() {
- return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
- + " / [ServiceCategories]"
- + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
- + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+ return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
+ + "|Mnc-" + mMnc
+ + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+ + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
}
@Override
@@ -373,6 +393,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
* The priority of sources are defined as follows:
* EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
* EMERGENCY_NUMBER_SOURCE_SIM >
+ * EMERGENCY_NUMBER_SOURCE_DATABASE >
* EMERGENCY_NUMBER_SOURCE_DEFAULT >
* EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
*
@@ -385,7 +406,9 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
score += 1 << 3;
}
- // TODO add a score if the number comes from Google's emergency number database
+ if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+ score += 1 << 2;
+ }
if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
score += 1 << 1;
}
@@ -412,14 +435,104 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
< emergencyNumber.getDisplayPriorityScore()) {
return 1;
} else {
- /**
- * TODO if both numbers have the same display priority score, the number matches the
- * Google's emergency number database has a higher display priority.
- */
return 0;
}
}
+ /**
+ * In-place merge same emergency numbers in the emergency number list.
+ *
+ * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+ * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+ * for the same EmergencyNumber.
+ *
+ * @param emergencyNumberList the emergency number list to process
+ *
+ * @hide
+ */
+ public static void mergeSameNumbersInEmergencyNumberList(
+ List<EmergencyNumber> emergencyNumberList) {
+ if (emergencyNumberList == null) {
+ return;
+ }
+ Set<EmergencyNumber> mergedEmergencyNumber = new HashSet<>();
+ for (int i = 0; i < emergencyNumberList.size(); i++) {
+ // Skip the check because it was merged.
+ if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+ continue;
+ }
+ for (int j = i + 1; j < emergencyNumberList.size(); j++) {
+ if (isSameEmergencyNumber(
+ emergencyNumberList.get(i), emergencyNumberList.get(j))) {
+ Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
+ + emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
+ // Set the merged emergency number in the current position
+ emergencyNumberList.set(i, mergeNumbers(
+ emergencyNumberList.get(i), emergencyNumberList.get(j)));
+ // Mark the emergency number has been merged
+ mergedEmergencyNumber.add(emergencyNumberList.get(j));
+ }
+ }
+ }
+ // Remove the marked emergency number in the orignal list
+ for (int i = 0; i < emergencyNumberList.size(); i++) {
+ if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+ emergencyNumberList.remove(i--);
+ }
+ }
+ }
+
+ /**
+ * Check if two emergency numbers are the same.
+ *
+ * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+ * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+ * for the same EmergencyNumber.
+ *
+ * @param first first EmergencyNumber to compare
+ * @param second second EmergencyNumber to compare
+ * @return true if they are the same EmergencyNumbers; false otherwise.
+ *
+ * @hide
+ */
+ public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
+ if (!first.getNumber().equals(second.getNumber())) {
+ return false;
+ }
+ if (!first.getCountryIso().equals(second.getCountryIso())) {
+ return false;
+ }
+ if (!first.getMnc().equals(second.getMnc())) {
+ return false;
+ }
+ if (first.getEmergencyServiceCategoryBitmask()
+ != second.getEmergencyServiceCategoryBitmask()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get a merged EmergencyNumber for two numbers if they are the same.
+ *
+ * @param first first EmergencyNumber to compare
+ * @param second second EmergencyNumber to compare
+ * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
+ *
+ * @hide
+ */
+ public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
+ if (isSameEmergencyNumber(first, second)) {
+ return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
+ first.getEmergencyServiceCategoryBitmask(),
+ first.getEmergencyNumberSourceBitmask()
+ | second.getEmergencyNumberSourceBitmask());
+ }
+ return null;
+ }
+
public static final Parcelable.Creator<EmergencyNumber> CREATOR =
new Parcelable.Creator<EmergencyNumber>() {
@Override
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 8fcdb6e90569..f1245959b3a8 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -16,11 +16,15 @@
package android.telephony.ims;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class enables an application to get details on why a method call failed.
*
@@ -30,158 +34,292 @@ import android.os.Parcelable;
public final class ImsReasonInfo implements Parcelable {
/**
- * Specific code of each types
+ * The Reason is unspecified.
*/
public static final int CODE_UNSPECIFIED = 0;
+
+ // LOCAL
+
+ // IMS -> Telephony
/**
- * LOCAL
+ * The passed argument is invalid.
*/
- // IMS -> Telephony
- // The passed argument is an invalid
public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
- // The operation is invoked in invalid call state
+ /**
+ * The operation was invoked while in an invalid call state.
+ */
public static final int CODE_LOCAL_ILLEGAL_STATE = 102;
- // IMS service internal error
+ /**
+ * IMS service internal error
+ */
public static final int CODE_LOCAL_INTERNAL_ERROR = 103;
- // IMS service goes down (service connection is lost)
+ /**
+ * ImsService has crashed (service connection is lost).
+ */
public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106;
- // No pending incoming call exists
+ /**
+ * No pending incoming call exists
+ */
public static final int CODE_LOCAL_NO_PENDING_CALL = 107;
- // IMS Call ended during conference merge process
+ /**
+ * IMS Call ended during conference merge process
+ */
public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108;
// IMS -> Telephony
- // Service unavailable; by power off
+ /**
+ * Service unavailable; radio power off
+ */
public static final int CODE_LOCAL_POWER_OFF = 111;
- // Service unavailable; by low battery
+ /**
+ * Service unavailable; low battery
+ */
public static final int CODE_LOCAL_LOW_BATTERY = 112;
- // Service unavailable; by out of service (data service state)
+ /**
+ * Service unavailable; out of service (data service state)
+ */
public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
- // Service unavailable; by no LTE coverage
- // (VoLTE is not supported even though IMS is registered)
+ /**
+ * Service unavailable; no LTE coverage
+ * (VoLTE is not supported even though IMS is registered)
+ */
public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122;
- // Service unavailable; by located in roaming area
+ /**
+ * Service unavailable; located in roaming area
+ */
public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
- // Service unavailable; by IP changed
+ /**
+ * Service unavailable; IP changed
+ */
public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
- // Service unavailable; other
+ /**
+ * Service unavailable; for an unspecified reason
+ */
public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
- // Service unavailable; IMS connection is lost (IMS is not registered)
+ /**
+ * Service unavailable; IMS is not registered
+ */
public static final int CODE_LOCAL_NOT_REGISTERED = 132;
// IMS <-> Telephony
- // Max call exceeded
+ /**
+ * Maximum number of simultaneous calls exceeded
+ */
public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
// IMS <- Telephony
- // Call busy
+ /**
+ * The call is busy.
+ */
public static final int CODE_LOCAL_CALL_BUSY = 142;
- // Call decline
+ /**
+ * The Call has been declined locally on this device.
+ */
public static final int CODE_LOCAL_CALL_DECLINE = 143;
// IMS -> Telephony
- // SRVCC is in progress
+ /**
+ * Can not complete call; an SRVCC is in progress.
+ */
public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
- // Resource reservation is failed (QoS precondition)
+ /**
+ * Can not complete call; resource reservation is failed (QoS precondition)
+ */
public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
- // Retry CS call; VoLTE service can't be provided by the network or remote end
- // Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ /**
+ * VoLTE service can't be provided by the network or remote end, retry the call.
+ * Resolve the extra code provided in (EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ */
public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
- // Retry VoLTE call; VoLTE service can't be provided by the network temporarily
+ /**
+ * VoLTE service can't be provided by the network temporarily, retry the call.
+ */
public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
- // IMS call is already terminated (in TERMINATED state)
+ /**
+ * IMS call is already terminated (in TERMINATED state).
+ */
public static final int CODE_LOCAL_CALL_TERMINATED = 148;
- // Handover not feasible
+ /**
+ * Call was disconnected because a handover is not feasible due to network conditions.
+ */
public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
- /**
+ /*
* TIMEOUT (IMS -> Telephony)
*/
- // 1xx waiting timer is expired after sending INVITE request (MO only)
+ /**
+ * 1xx waiting timer is expired after sending INVITE request (MO calls only)
+ */
public static final int CODE_TIMEOUT_1XX_WAITING = 201;
- // User no answer during call setup operation (MO/MT)
- // MO : 200 OK to INVITE request is not received,
- // MT : No action from user after alerting the call
+ /**
+ * User didn't answer during call setup operation (MO/MT)
+ * MO : 200 OK to INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
public static final int CODE_TIMEOUT_NO_ANSWER = 202;
- // User no answer during call update operation (MO/MT)
- // MO : 200 OK to re-INVITE request is not received,
- // MT : No action from user after alerting the call
+ /**
+ * User no answer during call update operation (MO/MT)
+ * MO : 200 OK to re-INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
- //Call was blocked by call barring
+ /**
+ * The call was blocked by call barring configuration.
+ */
public static final int CODE_CALL_BARRED = 240;
- //Call failures for FDN
+ /**
+ * The operation is restricted to fixed dialing numbers only.
+ */
public static final int CODE_FDN_BLOCKED = 241;
- // Network does not accept the emergency call request because IMEI was used as identification
- // and this capability is not supported by the network.
+ /**
+ * Network rejected the emergency call request because IMEI was used as identification
+ * and this capability is not supported by the network.
+ */
public static final int CODE_IMEI_NOT_ACCEPTED = 243;
//STK CC errors
+ /**
+ * Stk Call Control modified DIAL request to USSD request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+ /**
+ * Stk Call Control modified DIAL request to SS request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+ /**
+ * Stk Call Control modified DIAL request to DIAL with modified data.
+ */
public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+ /**
+ * Stk Call Control modified DIAL request to Video DIAL request.
+ */
public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+ /**
+ * Stk Call Control modified Video DIAL request to DIAL request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+ /**
+ * Stk Call Control modified Video DIAL request to Video DIAL request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+ /**
+ * Stk Call Control modified Video DIAL request to SS request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+ /**
+ * Stk Call Control modified Video DIAL request to USSD request.
+ */
public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
- /**
+ /*
* STATUSCODE (SIP response code) (IMS -> Telephony)
*/
// 3xx responses
- // SIP request is redirected
+ /**
+ * SIP 3xx response: SIP request is redirected
+ */
public static final int CODE_SIP_REDIRECTED = 321;
// 4xx responses
- // 400 : Bad Request
+ /**
+ * Sip 400 response : Bad Request
+ */
public static final int CODE_SIP_BAD_REQUEST = 331;
- // 403 : Forbidden
+ /**
+ * Sip 403 response : Forbidden
+ */
public static final int CODE_SIP_FORBIDDEN = 332;
- // 404 : Not Found
+ /**
+ * Sip 404 response : Not Found
+ */
public static final int CODE_SIP_NOT_FOUND = 333;
- // 415 : Unsupported Media Type
- // 416 : Unsupported URI Scheme
- // 420 : Bad Extension
+ /**
+ * Not supported, because of one of the following:
+ * SIP response 415 : Unsupported Media Type,
+ * SIP response 416 : Unsupported URI Scheme,
+ * SIP response 420 : Bad Extension
+ */
public static final int CODE_SIP_NOT_SUPPORTED = 334;
- // 408 : Request Timeout
+ /**
+ * SIP response 408 : Request Timeout.
+ */
public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
- // 480 : Temporarily Unavailable
+ /**
+ * SIP response 480 : Temporarily Unavailable
+ */
public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
- // 484 : Address Incomplete
+ /**
+ * SIP response 484 : Address Incomplete
+ */
public static final int CODE_SIP_BAD_ADDRESS = 337;
- // 486 : Busy Here
- // 600 : Busy Everywhere
+ /**
+ * Returned a busy response, may be one of the following:
+ * SIP response 486 : Busy Here,
+ * SIP response 600 : Busy Everywhere
+ */
public static final int CODE_SIP_BUSY = 338;
- // 487 : Request Terminated
+ /**
+ * SIP response 487 : Request Terminated
+ */
public static final int CODE_SIP_REQUEST_CANCELLED = 339;
- // 406 : Not Acceptable
- // 488 : Not Acceptable Here
- // 606 : Not Acceptable
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 406 : Not Acceptable
+ * SIP response 488 : Not Acceptable Here
+ * SIP response 606 : Not Acceptable
+ */
public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
- // 410 : Gone
- // 604 : Does Not Exist Anywhere
+ /**
+ * Received a not acceptable response, will be one of the following:
+ * SIP response 410 : Gone
+ * SIP response 604 : Does Not Exist Anywhere
+ */
public static final int CODE_SIP_NOT_REACHABLE = 341;
- // Others
+ /**
+ * Received another unspecified error SIP response from the client.
+ */
public static final int CODE_SIP_CLIENT_ERROR = 342;
- // 481 Transaction Does Not Exist
+ /**
+ * SIP response 481: Transaction Does Not Exist
+ */
public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343;
// 5xx responses
- // 501 : Server Internal Error
+ /**
+ * SIP response 501 : Server Internal Error
+ */
public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
- // 503 : Service Unavailable
+ /**
+ * SIP response 503 : Service Unavailable
+ */
public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
- // 504 : Server Time-out
+ /**
+ * SIP response 504 : Server Time-out
+ */
public static final int CODE_SIP_SERVER_TIMEOUT = 353;
- // Others
+ /**
+ * Received an unspecified SIP server error response.
+ */
public static final int CODE_SIP_SERVER_ERROR = 354;
// 6xx responses
- // 603 : Decline
+ /**
+ * 603 : Decline
+ */
public static final int CODE_SIP_USER_REJECTED = 361;
- // Others
+ /**
+ * Unspecified 6xx error.
+ */
public static final int CODE_SIP_GLOBAL_ERROR = 362;
- // Emergency failure
+
+ /**
+ * Emergency call failed in the modem with a temporary fail cause and should be redialed on this
+ * slot.
+ */
public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+ /**
+ * Emergency call failed in the modem with a permanent fail cause and should not be redialed on
+ * this slot. If there are any other slots available for emergency calling, try those.
+ */
public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
/**
@@ -193,132 +331,188 @@ public final class ImsReasonInfo implements Parcelable {
*/
public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
- /**
+ /*
* MEDIA (IMS -> Telephony)
*/
- // Media resource initialization failed
+ /**
+ * Media resource initialization failed
+ */
public static final int CODE_MEDIA_INIT_FAILED = 401;
- // RTP timeout (no audio / video traffic in the session)
+ /**
+ * RTP timeout (no audio / video traffic in the session)
+ */
public static final int CODE_MEDIA_NO_DATA = 402;
- // Media is not supported; so dropped the call
+ /**
+ * Media is not supported; so dropped the call
+ */
public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403;
- // Unknown media related errors
+ /**
+ * Unspecified media related error.
+ */
public static final int CODE_MEDIA_UNSPECIFIED = 404;
- /**
+ /*
* USER
*/
// Telephony -> IMS
- // User triggers the call end
+ /**
+ * User triggers the call to be terminated.
+ */
public static final int CODE_USER_TERMINATED = 501;
- // No action while an incoming call is ringing
+ /**
+ * No action was taken while an incoming call was ringing.
+ */
public static final int CODE_USER_NOANSWER = 502;
- // User ignores an incoming call
+ /**
+ * User ignored an incoming call.
+ */
public static final int CODE_USER_IGNORE = 503;
- // User declines an incoming call
+ /**
+ * User declined an incoming call.
+ */
public static final int CODE_USER_DECLINE = 504;
- // Device declines/ends a call due to low battery
+ /**
+ * Device declined/ended a call due to a low battery condition.
+ */
public static final int CODE_LOW_BATTERY = 505;
- // Device declines call due to blacklisted call ID
+ /**
+ * Device declined a call due to a blacklisted caller ID.
+ */
public static final int CODE_BLACKLISTED_CALL_ID = 506;
// IMS -> Telephony
- // The call is terminated by the network or remote user
- public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
-
/**
- * Extra codes for the specific code value
- * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+ * The call has been terminated by the network or remote user.
*/
- // Try to connect CS call; normal
- public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
- // Try to connect CS call without the notification to user
- public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
- // Try to connect CS call by the settings of the menu
- public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+ public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
- /**
+ /*
* UT
*/
+ /**
+ * UT is currently not supported on this device.
+ */
public static final int CODE_UT_NOT_SUPPORTED = 801;
+ /**
+ * UT services are currently not available on this device.
+ */
public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+ /**
+ * The requested UT operation is not allowed.
+ */
public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+ /**
+ * The UT request resulted in a network error.
+ */
public static final int CODE_UT_NETWORK_ERROR = 804;
+ /**
+ * The password entered for UT operations does not match the stored password.
+ */
public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
//STK CC errors
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a dial command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a USSD command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to another supplementary service command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+ /**
+ * Sim Toolkit Call Control modified the UT operation to a video call dial command.
+ */
public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+ /**@hide*/
+ @IntDef(value = {
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO
+ }, prefix = "CODE_UT_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtReason {}
+
/**
- * ECBM
+ * Emergency callback mode is not supported.
*/
public static final int CODE_ECBM_NOT_SUPPORTED = 901;
/**
- * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ * Fail code used to indicate that Multi-endpoint is not supported by the IMS framework.
*/
public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902;
/**
- * Ims Registration error code
+ * IMS Registration error code
*/
public static final int CODE_REGISTRATION_ERROR = 1000;
- /**
+ /*
* CALL DROP error codes (Call could drop because of many reasons like Network not available,
* handover, failed, etc)
*/
-
/**
- * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
- * active wifi call and at the edge of coverage and there is no qualified LTE network available
- * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
- * code is received as part of the handover message.
- */
- public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
-
- /**
- * MT call has ended due to a release from the network
- * because the call was answered elsewhere
+ * MT call has ended due to a release from the network because the call was answered elsewhere.
*/
public static final int CODE_ANSWERED_ELSEWHERE = 1014;
/**
- * For MultiEndpoint - Call Pull request has failed
+ * For MultiEndpoint - Call Pull request has failed.
*/
public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
/**
- * For MultiEndpoint - Call has been pulled from primary to secondary
+ * For MultiEndpoint - Call has been pulled from primary to secondary.
*/
public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
/**
- * Supplementary services (HOLD/RESUME) failure error codes.
- * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+ * active wifi call and at the edge of coverage and there is no qualified LTE network available
+ * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+ * code is received as part of the handover message.
+ */
+ public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
+
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command failed.
*/
public static final int CODE_SUPP_SVC_FAILED = 1201;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command was cancelled.
+ */
public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+ /**
+ * Supplementary Services (HOLD/RESUME) - the command resulted in a re-invite collision.
+ */
public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
/**
- * DPD Procedure received no response or send failed
+ * DPD Procedure received no response or send failed.
*/
public static final int CODE_IWLAN_DPD_FAILURE = 1300;
/**
- * Establishment of the ePDG Tunnel Failed
+ * Establishment of the ePDG Tunnel Failed.
*/
public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400;
/**
- * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ * Re-keying of the ePDG Tunnel Failed; may not always result in teardown.
*/
public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401;
/**
- * Connection to the packet gateway is lost
+ * Connection to the packet gateway is lost.
*/
public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
@@ -571,8 +765,10 @@ public final class ImsReasonInfo implements Parcelable {
*/
public static final int CODE_REJECT_ONGOING_CS_CALL = 1621;
- /* OEM specific error codes. To be used by OEMs when they don't want to
- reveal error code which would be replaced by ERROR_UNSPECIFIED */
+ /*
+ * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
+ * would be replaced by ERROR_UNSPECIFIED.
+ */
public static final int CODE_OEM_CAUSE_1 = 0xf001;
public static final int CODE_OEM_CAUSE_2 = 0xf002;
public static final int CODE_OEM_CAUSE_3 = 0xf003;
@@ -597,6 +793,33 @@ public final class ImsReasonInfo implements Parcelable {
= "Forbidden. Not Authorized for Service";
+ /*
+ * Extra codes for the specific code value
+ * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+ */
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS and do not notify the user.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+ /**
+ * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+ * been returned.
+ * <p>
+ * Try to connect the call using CS by using the settings.
+ */
+ public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+
// For main reason code
/** @hide */
@UnsupportedAppUsage
@@ -638,29 +861,28 @@ public final class ImsReasonInfo implements Parcelable {
}
/**
- *
+ * @return an integer representing more information about the completion of an operation.
*/
public int getCode() {
return mCode;
}
/**
- *
+ * @return an optional OEM specified code that provides extra information.
*/
public int getExtraCode() {
return mExtraCode;
}
/**
- *
+ * @return an optional OEM specified string that provides extra information about the operation
+ * result.
*/
public String getExtraMessage() {
return mExtraMessage;
}
/**
- * Returns the string format of {@link ImsReasonInfo}
- *
* @return the string format of {@link ImsReasonInfo}
*/
public String toString() {
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index db5ba4784621..3a82517955ce 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -65,6 +65,17 @@ public final class ImsSsData implements Parcelable {
public static final int SS_INCOMING_BARRING_DN = 21;
public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ACTIVATION,
+ SS_DEACTIVATION,
+ SS_INTERROGATION,
+ SS_REGISTRATION,
+ SS_ERASURE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType{}
+
//Supplementary Service Request Types
public static final int SS_ACTIVATION = 0;
public static final int SS_DEACTIVATION = 1;
@@ -72,6 +83,17 @@ public final class ImsSsData implements Parcelable {
public static final int SS_REGISTRATION = 3;
public static final int SS_ERASURE = 4;
+ /**@hide*/
+ @IntDef(flag = true, prefix = {"SS_"}, value = {
+ SS_ALL_TELE_AND_BEARER_SERVICES,
+ SS_ALL_TELESEVICES,
+ SS_TELEPHONY,
+ SS_ALL_DATA_TELESERVICES,
+ SS_SMS_SERVICES,
+ SS_ALL_TELESERVICES_EXCEPT_SMS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TeleserviceType{}
+
// Supplementary Service Teleservice Type
public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
public static final int SS_ALL_TELESEVICES = 1;
@@ -191,21 +213,6 @@ public final class ImsSsData implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceType{}
- /** @hide */
- @IntDef(flag = true, prefix = { "SERVICE_CLASS" }, value = {
- SERVICE_CLASS_NONE,
- SERVICE_CLASS_VOICE,
- SERVICE_CLASS_DATA,
- SERVICE_CLASS_FAX,
- SERVICE_CLASS_SMS,
- SERVICE_CLASS_DATA_CIRCUIT_SYNC,
- SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
- SERVICE_CLASS_DATA_PACKET_ACCESS,
- SERVICE_CLASS_DATA_PAD
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ServiceClass{}
-
/**
* The Service type of this Supplementary service.
* @hide
@@ -221,7 +228,7 @@ public final class ImsSsData implements Parcelable {
* {@link #SS_ERASURE}
* @hide
*/
- public final int requestType;
+ public final @RequestType int requestType;
/**
* Supplementary Service teleservice type:
@@ -234,14 +241,14 @@ public final class ImsSsData implements Parcelable {
*
* @hide
*/
- public final int teleserviceType;
+ public final @TeleserviceType int teleserviceType;
/**
* Supplementary Service service class.
*
* @hide
*/
- public final @ServiceClass int serviceClass;
+ public final @ServiceClassFlags int serviceClass;
/**
* Result of Supplementary Service operation. Valid values are:
@@ -285,7 +292,7 @@ public final class ImsSsData implements Parcelable {
* @see #build()
*/
public Builder(@ServiceType int serviceType, int requestType, int teleserviceType,
- @ServiceClass int serviceClass, int result) {
+ @ServiceClassFlags int serviceClass, int result) {
mImsSsData = new ImsSsData(serviceType, requestType, teleserviceType, serviceClass,
result);
}
@@ -294,7 +301,7 @@ public final class ImsSsData implements Parcelable {
* Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
* data.
*/
- public Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
+ public @NonNull Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
mImsSsData.mImsSsInfo = imsSsInfos;
return this;
}
@@ -303,7 +310,8 @@ public final class ImsSsData implements Parcelable {
* Set the array of {@link ImsCallForwardInfo}s that are associated with this supplementary
* service data.
*/
- public Builder setCallForwardingInfo(@NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
+ public @NonNull Builder setCallForwardingInfo(
+ @NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
mImsSsData.mCfInfo = imsCallForwardInfos;
return this;
}
@@ -311,7 +319,7 @@ public final class ImsSsData implements Parcelable {
/**
* @return an {@link ImsSsData} containing optional parameters.
*/
- public ImsSsData build() {
+ public @NonNull ImsSsData build() {
return mImsSsData;
}
}
@@ -337,7 +345,7 @@ public final class ImsSsData implements Parcelable {
* success, or ImsReasonInfo code if the result is a failure.
*/
public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
- @ServiceClass int serviceClass, int result) {
+ @ServiceClassFlags int serviceClass, int result) {
this.serviceType = serviceType;
this.requestType = requestType;
this.teleserviceType = teleserviceType;
@@ -449,14 +457,9 @@ public final class ImsSsData implements Parcelable {
}
/**
- * Supplementary Service request Type:
- * {@link #SS_ACTIVATION),
- * {@link #SS_DEACTIVATION},
- * {@link #SS_INTERROGATION},
- * {@link #SS_REGISTRATION},
- * {@link #SS_ERASURE}
+ * Supplementary Service request Type.
*/
- public int getRequestType() {
+ public @RequestType int getRequestType() {
return requestType;
}
@@ -468,31 +471,25 @@ public final class ImsSsData implements Parcelable {
}
/**
- * Supplementary Service teleservice type:
- * {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
- * {@link #SS_ALL_TELESEVICES},
- * {@link #SS_TELEPHONY},
- * {@link #SS_ALL_DATA_TELESERVICES},
- * {@link #SS_SMS_SERVICES},
- * {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+ * Supplementary Service teleservice type.
*/
- public int getTeleserviceType() {
+ public @TeleserviceType int getTeleserviceType() {
return teleserviceType;
}
/**
* Supplementary Service service class.
*/
- public @ServiceClass int getServiceClass() {
+ public @ServiceClassFlags int getServiceClass() {
return serviceClass;
}
/**
* Result of Supplementary Service operation. Valid values are:
* {@link #RESULT_SUCCESS} if the result is success, or
- * {@link ImsReasonInfo} CODE_* code if the result is a failure.
+ * {@link ImsReasonInfo.UtReason} code if the result is a failure.
*/
- public int getResult() {
+ public @ImsReasonInfo.UtReason int getResult() {
return result;
}
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 0af6e6298c87..031f9e10175b 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -82,6 +82,7 @@ public final class ImsSsInfo implements Parcelable {
*/
public static final int SERVICE_PROVISIONED = 1;
+ /**@hide*/
@IntDef(value = {
CLIR_OUTGOING_DEFAULT,
CLIR_OUTGOING_INVOCATION,
@@ -141,6 +142,7 @@ public final class ImsSsInfo implements Parcelable {
*/
public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4;
+ /**@hide*/
@IntDef(value = {
CLIR_STATUS_NOT_PROVISIONED,
CLIR_STATUS_PROVISIONED_PERMANENT,
@@ -184,7 +186,7 @@ public final class ImsSsInfo implements Parcelable {
* Set the ICB number for IMS call barring.
* @param number The number in E.164 international format.
*/
- public Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
+ public @NonNull Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
mImsSsInfo.mIcbNum = number;
return this;
}
@@ -192,7 +194,7 @@ public final class ImsSsInfo implements Parcelable {
/**
* Set the provisioning status for a Supplementary Service interrogation response.
*/
- public Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
+ public @NonNull Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
mImsSsInfo.mProvisionStatus = provisionStatus;
return this;
}
@@ -201,7 +203,7 @@ public final class ImsSsInfo implements Parcelable {
* Set the Calling Line Identification Restriction (CLIR) status for a supplementary service
* interrogation response.
*/
- public Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
+ public @NonNull Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
mImsSsInfo.mClirInterrogationStatus = status;
return this;
}
@@ -209,7 +211,7 @@ public final class ImsSsInfo implements Parcelable {
/**
* Set the Calling line identification Restriction (CLIR) state for outgoing calls.
*/
- public Builder setClirOutgoingState(@ClirOutgoingState int state) {
+ public @NonNull Builder setClirOutgoingState(@ClirOutgoingState int state) {
mImsSsInfo.mClirOutgoingState = state;
return this;
}
@@ -217,7 +219,7 @@ public final class ImsSsInfo implements Parcelable {
/**
* @return a built {@link ImsSsInfo} containing optional the parameters that were set.
*/
- public ImsSsInfo build() {
+ public @NonNull ImsSsInfo build() {
return mImsSsInfo;
}
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 0abe45ce448c..13539b855de2 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
@@ -32,15 +33,15 @@ import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.RawContacts;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import com.android.i18n.phonenumbers.NumberParseException;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import android.telephony.SubscriptionManager;
+import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import java.util.Locale;
@@ -112,6 +113,9 @@ public class CallerInfo {
public Uri contactRefUri;
public String lookupKey;
+ public ComponentName preferredPhoneAccountComponent;
+ public String preferredPhoneAccountId;
+
public long userType;
/**
@@ -264,6 +268,17 @@ public class CallerInfo {
info.contactDisplayPhotoUri = null;
}
+ columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.preferredPhoneAccountComponent =
+ ComponentName.unflattenFromString(cursor.getString(columnIndex));
+ }
+
+ columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_ID);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.preferredPhoneAccountId = cursor.getString(columnIndex);
+ }
+
// look for the custom ringtone, create from the string stored
// in the database.
// An empty string ("") in the database indicates a silent ringtone,
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 79f0635c67f7..78fc0bc487bf 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -25,6 +25,7 @@ import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.emergency.EmergencyNumber;
oneway interface IPhoneStateListener {
void onServiceStateChanged(in ServiceState serviceState);
@@ -53,5 +54,6 @@ oneway interface IPhoneStateListener {
void onPhoneCapabilityChanged(in PhoneCapability capability);
void onPreferredDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
+ void onEmergencyNumberListChanged(in Map emergencyNumberList);
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index f9db4b0afd12..65d1a920a324 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -210,6 +210,10 @@ interface ISub {
*/
List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage);
+ boolean removeSubscriptionsFromGroup(in int[] subIdList, String callingPackage);
+
+ List<SubscriptionInfo> getSubscriptionsInGroup(int subId, String callingPackage);
+
int getSlotIndex(int subId);
int[] getSubId(int slotIndex);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 399dc5255176..88b9302afacb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1716,7 +1716,7 @@ interface ITelephony {
/**
* Identify if the number is emergency number, based on all the active subscriptions.
*/
- boolean isCurrentEmergencyNumber(String number);
+ boolean isCurrentEmergencyNumber(String number, boolean exactMatch);
/**
* Return a list of certs in hex string from loaded carrier privileges access rules.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 76e7509c1094..d9f5c3f6d0fa 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -81,5 +81,5 @@ interface ITelephonyRegistry {
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
void notifyPreferredDataSubIdChanged(int preferredSubId);
void notifyRadioPowerStateChanged(in int state);
- void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
+ void notifyEmergencyNumberList();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cb8269efe443..c9343171e03e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -483,4 +483,5 @@ public interface RILConstants {
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
+ int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
}
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 50aef1d24faf..84f735985b0d 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -474,7 +474,7 @@ public class NetworkCapabilitiesTest {
new StringNetworkSpecifier("specs"));
try {
nc2.addTransportType(TRANSPORT_WIFI);
- fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
+ fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
} catch (IllegalStateException expected) {
// empty
}
@@ -500,16 +500,23 @@ public class NetworkCapabilitiesTest {
// empty
});
NetworkCapabilities nc2 = new NetworkCapabilities();
+ // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
+ // combine fails)
nc2.setTransportInfo(new TransportInfo() {
// empty
});
try {
nc1.combineCapabilities(nc2);
- fail("Should not be able to combine NetworkCaabilities which contain TransportInfos");
+ fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
} catch (IllegalStateException expected) {
// empty
}
+
+ // verify that can combine with identical TransportInfo objects
+ NetworkCapabilities nc3 = new NetworkCapabilities();
+ nc3.setTransportInfo(nc1.getTransportInfo());
+ nc1.combineCapabilities(nc3);
}
private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 3a71e836aa38..2ef8b999a192 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -230,7 +230,7 @@ TEST_F(ConfigurationParserTest, ValidateFile) {
test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de")));
ASSERT_TRUE(a1.android_sdk);
ASSERT_TRUE(a1.android_sdk.value().min_sdk_version);
- EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19L);
EXPECT_THAT(a1.textures, SizeIs(1ul));
EXPECT_THAT(a1.features, SizeIs(1ul));
@@ -250,7 +250,7 @@ TEST_F(ConfigurationParserTest, ValidateFile) {
test::ParseConfigOrDie("fr-rCA")));
ASSERT_TRUE(a2.android_sdk);
ASSERT_TRUE(a2.android_sdk.value().min_sdk_version);
- EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19L);
EXPECT_THAT(a2.textures, SizeIs(1ul));
EXPECT_THAT(a2.features, SizeIs(1ul));
}
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2291e5a92730..4ffcf6846947 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -11,6 +11,7 @@ LIBCORE_PACKAGES="\
android.system \
com.android.bouncycastle \
com.android.conscrypt \
+ com.android.i18n.phonenumbers \
com.android.okhttp \
com.sun \
dalvik \
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 9dceba2163eb..6b4c34677d96 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -155,7 +155,7 @@ def dump_representative_locales(representative_locales):
print
print 'std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({'
for locale in sorted(representative_locales):
- print ' 0x%08Xllu, // %s' % (
+ print ' 0x%08XLLU, // %s' % (
pack_to_uint64(locale),
locale)
print '});'
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
new file mode 100644
index 000000000000..ca6b3c4572f5
--- /dev/null
+++ b/tools/processors/view_inspector/Android.bp
@@ -0,0 +1,27 @@
+java_library_host {
+ name: "view-inspector-annotation-processor",
+
+ srcs: ["src/java/**/*.java"],
+ java_resource_dirs: ["src/resources"],
+
+ static_libs: [
+ "javapoet",
+ ],
+
+ use_tools_jar: true,
+}
+
+java_test_host {
+ name: "view-inspector-annotation-processor-test",
+
+ srcs: ["test/java/**/*.java"],
+ java_resource_dirs: ["test/resources"],
+
+ static_libs: [
+ "guava",
+ "junit",
+ "view-inspector-annotation-processor",
+ ],
+
+ test_suites: ["general-tests"],
+}
diff --git a/tools/processors/view_inspector/OWNERS b/tools/processors/view_inspector/OWNERS
new file mode 100644
index 000000000000..0473f54e57ca
--- /dev/null
+++ b/tools/processors/view_inspector/OWNERS
@@ -0,0 +1,3 @@
+alanv@google.com
+ashleyrose@google.com
+aurimas@google.com \ No newline at end of file
diff --git a/tools/processors/view_inspector/TEST_MAPPING b/tools/processors/view_inspector/TEST_MAPPING
new file mode 100644
index 000000000000..a91b5b452c39
--- /dev/null
+++ b/tools/processors/view_inspector/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "view-inspector-annotation-processor-test"
+ }
+ ]
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
new file mode 100644
index 000000000000..f157949f4d1b
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import java.util.Map;
+import java.util.Optional;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * Utilities for working with {@link AnnotationMirror}.
+ */
+final class AnnotationUtils {
+ private final Elements mElementUtils;
+ private final Types mTypeUtils;
+
+ AnnotationUtils(ProcessingEnvironment processingEnv) {
+ mElementUtils = processingEnv.getElementUtils();
+ mTypeUtils = processingEnv.getTypeUtils();
+ }
+
+ /**
+ * Get a {@link AnnotationMirror} specified by name from an {@link Element}.
+ *
+ * @param qualifiedName The fully qualified name of the annotation to search for
+ * @param element The element to search for annotations on
+ * @return The mirror of the requested annotation
+ * @throws ProcessingException If there is not exactly one of the requested annotation.
+ */
+ AnnotationMirror exactlyOneMirror(String qualifiedName, Element element) {
+ final Element targetTypeElment = mElementUtils.getTypeElement(qualifiedName);
+ final TypeMirror targetType = targetTypeElment.asType();
+ AnnotationMirror result = null;
+
+ for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+ final TypeMirror annotationType = annotation.getAnnotationType().asElement().asType();
+ if (mTypeUtils.isSameType(annotationType, targetType)) {
+ if (result == null) {
+ result = annotation;
+ } else {
+ final String message = String.format(
+ "Element had multiple instances of @%s, expected exactly one",
+ targetTypeElment.getSimpleName());
+
+ throw new ProcessingException(message, element, annotation);
+ }
+ }
+ }
+
+ if (result == null) {
+ final String message = String.format(
+ "Expected an @%s annotation, found none", targetTypeElment.getSimpleName());
+ throw new ProcessingException(message, element);
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * Extract a string-valued property from an {@link AnnotationMirror}.
+ *
+ * @param propertyName The name of the requested property
+ * @param annotationMirror The mirror to search for the property
+ * @return The String value of the annotation or null
+ */
+ Optional<String> stringProperty(String propertyName, AnnotationMirror annotationMirror) {
+ final AnnotationValue value = valueByName(propertyName, annotationMirror);
+ if (value != null) {
+ return Optional.of((String) value.getValue());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+
+ /**
+ * Extract a {@link AnnotationValue} from a mirror by string property name.
+ *
+ * @param propertyName The name of the property requested property
+ * @param annotationMirror
+ * @return
+ */
+ AnnotationValue valueByName(String propertyName, AnnotationMirror annotationMirror) {
+ final Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap =
+ annotationMirror.getElementValues();
+
+ for (ExecutableElement method : valueMap.keySet()) {
+ if (method.getSimpleName().contentEquals(propertyName)) {
+ return valueMap.get(method);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
new file mode 100644
index 000000000000..579745d2aaef
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import com.squareup.javapoet.ClassName;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Model of an inspectable class derived from annotations.
+ *
+ * This class does not use any {@code javax.lang.model} objects to facilitate building models for
+ * testing {@link InspectionCompanionGenerator}.
+ */
+public final class InspectableClassModel {
+ private final ClassName mClassName;
+ private final Map<String, Property> mPropertyMap;
+ private Optional<String> mNodeName = Optional.empty();
+
+ /**
+ * @param className The name of the modeled class
+ */
+ public InspectableClassModel(ClassName className) {
+ mClassName = className;
+ mPropertyMap = new HashMap<>();
+ }
+
+ public ClassName getClassName() {
+ return mClassName;
+ }
+
+ public Optional<String> getNodeName() {
+ return mNodeName;
+ }
+
+ public void setNodeName(Optional<String> nodeName) {
+ mNodeName = nodeName;
+ }
+
+ /**
+ * Add a property to the model, replacing an existing property of the same name.
+ *
+ * @param property The property to add or replace
+ */
+ public void putProperty(Property property) {
+ mPropertyMap.put(property.getName(), property);
+ }
+
+ /**
+ * Get a property by name.
+ *
+ * @param name The name of the property
+ * @return The property or an empty optional
+ */
+ public Optional<Property> getProperty(String name) {
+ return Optional.of(mPropertyMap.get(name));
+ }
+
+ /**
+ * Get all the properties defined on this model.
+ *
+ * @return An un-ordered collection of properties
+ */
+ public Collection<Property> getAllProperties() {
+ return mPropertyMap.values();
+ }
+
+ /**
+ * Model an inspectable property
+ */
+ public static final class Property {
+ private final String mName;
+ private String mGetter;
+ private Type mType;
+ private boolean mAttributeIdInferrableFromR = true;
+ private int mAttributeId = 0;
+
+ public Property(String name) {
+ mName = name;
+ }
+
+ public int getAttributeId() {
+ return mAttributeId;
+ }
+
+ /**
+ * Set the attribute ID, and mark the attribute ID as non-inferrable.
+ *
+ * @param attributeId The attribute ID for this property
+ */
+ public void setAttributeId(int attributeId) {
+ mAttributeIdInferrableFromR = false;
+ mAttributeId = attributeId;
+ }
+
+ public boolean isAttributeIdInferrableFromR() {
+ return mAttributeIdInferrableFromR;
+ }
+
+ public void setAttributeIdInferrableFromR(boolean attributeIdInferrableFromR) {
+ mAttributeIdInferrableFromR = attributeIdInferrableFromR;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getGetter() {
+ return mGetter;
+ }
+
+ public void setGetter(String getter) {
+ mGetter = getter;
+ }
+
+ public Type getType() {
+ return mType;
+ }
+
+ public void setType(Type type) {
+ mType = type;
+ }
+
+ public enum Type {
+ /** Primitive or boxed {@code boolean} */
+ BOOLEAN,
+
+ /** Primitive or boxed {@code byte} */
+ BYTE,
+
+ /** Primitive or boxed {@code char} */
+ CHAR,
+
+ /** Primitive or boxed {@code double} */
+ DOUBLE,
+
+ /** Primitive or boxed {@code float} */
+ FLOAT,
+
+ /** Primitive or boxed {@code int} */
+ INT,
+
+ /** Primitive or boxed {@code long} */
+ LONG,
+
+ /** Primitive or boxed {@code short} */
+ SHORT,
+
+ /** Any other object */
+ OBJECT,
+
+ /**
+ * A color object or packed color {@code int} or {@code long}.
+ *
+ * @see android.graphics.Color
+ * @see android.annotation.ColorInt
+ * @see android.annotation.ColorLong
+ */
+ COLOR,
+
+ /**
+ * An {@code int} packed with a gravity specification
+ *
+ * @see android.view.Gravity
+ */
+ GRAVITY,
+
+ /**
+ * An enumeration packed into an {@code int}.
+ *
+ * @see android.view.inspector.IntEnumMapping
+ */
+ INT_ENUM,
+
+ /**
+ * Non-exclusive or partially-exclusive flags packed into an {@code int}.
+ *
+ * @see android.view.inspector.IntFlagMapping
+ */
+ INT_FLAG
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
new file mode 100644
index 000000000000..a186a82af160
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import java.util.Optional;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+/**
+ * Process {InspectableNodeName} annotations
+ *
+ * @see android.view.inspector.InspectableNodeName
+ */
+public final class InspectableNodeNameProcessor implements ModelProcessor {
+ private final String mQualifiedName;
+ private final ProcessingEnvironment mProcessingEnv;
+ private final AnnotationUtils mAnnotationUtils;
+
+ /**
+ * @param annotationQualifiedName The qualified name of the annotation to process
+ * @param processingEnv The processing environment from the parent processor
+ */
+ public InspectableNodeNameProcessor(
+ String annotationQualifiedName,
+ ProcessingEnvironment processingEnv) {
+ mQualifiedName = annotationQualifiedName;
+ mProcessingEnv = processingEnv;
+ mAnnotationUtils = new AnnotationUtils(processingEnv);
+ }
+
+ /**
+ * Set the node name on the model if one is supplied.
+ *
+ * If the model already has a different node name, the node name will not be updated, and
+ * the processor will print an error the the messager.
+ *
+ * @param element The annotated element to operate on
+ * @param model The model this element should be merged into
+ */
+ @Override
+ public void process(Element element, InspectableClassModel model) {
+ try {
+ final AnnotationMirror mirror =
+ mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
+ final Optional<String> nodeName = mAnnotationUtils.stringProperty("value", mirror);
+
+ if (!model.getNodeName().isPresent() || model.getNodeName().equals(nodeName)) {
+ model.setNodeName(nodeName);
+ } else {
+ final String message = String.format(
+ "Node name was already set to \"%s\", refusing to change it to \"%s\".",
+ model.getNodeName().get(),
+ nodeName);
+ throw new ProcessingException(message, element, mirror);
+ }
+ } catch (ProcessingException processingException) {
+ processingException.print(mProcessingEnv.getMessager());
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
new file mode 100644
index 000000000000..3b85dbb77d5c
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import android.processor.view.inspector.InspectableClassModel.Property;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.NameAllocator;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates a source file defining a {@link android.view.inspector.InspectionCompanion}.
+ */
+public final class InspectionCompanionGenerator {
+ private final Filer mFiler;
+ private final Class mRequestingClass;
+
+ /**
+ * The class name for {@code R.java}.
+ */
+ private static final ClassName R_CLASS_NAME = ClassName.get("android", "R");
+
+ /**
+ * The class name of {@link android.content.res.ResourceId}.
+ */
+ private static final ClassName RESOURCE_ID_CLASS_NAME = ClassName.get(
+ "android.content.res", "ResourceId");
+
+ /**
+ * The class name of {@link android.view.inspector.InspectionCompanion}.
+ */
+ private static final ClassName INSPECTION_COMPANION = ClassName.get(
+ "android.view.inspector", "InspectionCompanion");
+
+ /**
+ * The class name of {@link android.view.inspector.PropertyMapper}.
+ */
+ private static final ClassName PROPERTY_MAPPER = ClassName.get(
+ "android.view.inspector", "PropertyMapper");
+
+ /**
+ * The class name of {@link android.view.inspector.PropertyReader}.
+ */
+ private static final ClassName PROPERTY_READER = ClassName.get(
+ "android.view.inspector", "PropertyReader");
+
+ /**
+ * The {@code mPropertiesMapped} field.
+ */
+ private static final FieldSpec M_PROPERTIES_MAPPED = FieldSpec
+ .builder(TypeName.BOOLEAN, "mPropertiesMapped", Modifier.PRIVATE)
+ .initializer("false")
+ .addJavadoc(
+ "Set by {@link #mapProperties($T)} once properties have been mapped.\n",
+ PROPERTY_MAPPER)
+ .build();
+
+ /**
+ * The suffix of the generated class name after the class's binary name.
+ */
+ private static final String GENERATED_CLASS_SUFFIX = "$$InspectionCompanion";
+
+ /**
+ * The null resource ID.
+ *
+ * @see android.content.res.ResourceId#ID_NULL
+ */
+ private static final int NO_ID = 0;
+
+ /**
+ * @param filer A filer to write the generated source to
+ * @param requestingClass A class object representing the class that invoked the generator
+ */
+ public InspectionCompanionGenerator(Filer filer, Class requestingClass) {
+ mFiler = filer;
+ mRequestingClass = requestingClass;
+ }
+
+ /**
+ * Generate and write an inspection companion.
+ *
+ * @param model The model to generated
+ * @throws IOException From the Filer
+ */
+ public void generate(InspectableClassModel model) throws IOException {
+ generateFile(model).writeTo(mFiler);
+ }
+
+ /**
+ * Generate a {@link JavaFile} from a model.
+ *
+ * This is package-public for testing.
+ *
+ * @param model The model to generate from
+ * @return A generated file of an {@link android.view.inspector.InspectionCompanion}
+ */
+ JavaFile generateFile(InspectableClassModel model) {
+ return JavaFile
+ .builder(model.getClassName().packageName(), generateTypeSpec(model))
+ .indent(" ")
+ .build();
+ }
+
+ /**
+ * Generate a {@link TypeSpec} for the {@link android.view.inspector.InspectionCompanion}
+ * for the supplied model.
+ *
+ * @param model The model to generate from
+ * @return A TypeSpec of the inspection companion
+ */
+ private TypeSpec generateTypeSpec(InspectableClassModel model) {
+ final List<PropertyIdField> propertyIdFields = generatePropertyIdFields(model);
+
+ TypeSpec.Builder builder = TypeSpec
+ .classBuilder(generateClassName(model))
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addSuperinterface(ParameterizedTypeName.get(
+ INSPECTION_COMPANION, model.getClassName()))
+ .addJavadoc("Inspection companion for {@link $T}.\n\n", model.getClassName())
+ .addJavadoc("Generated by {@link $T}\n", getClass())
+ .addJavadoc("on behalf of {@link $T}.\n", mRequestingClass)
+ .addField(M_PROPERTIES_MAPPED);
+
+ for (PropertyIdField propertyIdField : propertyIdFields) {
+ builder.addField(propertyIdField.mFieldSpec);
+ }
+
+ builder.addMethod(generateMapProperties(propertyIdFields))
+ .addMethod(generateReadProperties(model, propertyIdFields));
+
+ generateGetNodeName(model).ifPresent(builder::addMethod);
+
+ return builder.build();
+ }
+
+ /**
+ * Build a list of {@link PropertyIdField}'s for a model.
+ *
+ * To insure idempotency of the generated code, this method sorts the list of properties
+ * alphabetically by name.
+ *
+ * A {@link NameAllocator} is used to ensure that the field names are valid Java identifiers,
+ * and it prevents overlaps in names by suffixing them as needed.
+ *
+ * @param model The model to get properties from
+ * @return A list of properties and fields
+ */
+ private List<PropertyIdField> generatePropertyIdFields(InspectableClassModel model) {
+ final NameAllocator nameAllocator = new NameAllocator();
+ final List<Property> sortedProperties = new ArrayList<>(model.getAllProperties());
+ final List<PropertyIdField> propertyIdFields = new ArrayList<>(sortedProperties.size());
+
+ sortedProperties.sort(Comparator.comparing(Property::getName));
+
+ for (Property property : sortedProperties) {
+ // Format a property to a member field name like "someProperty" -> "mSomePropertyId"
+ final String memberName = String.format(
+ "m%s%sId",
+ property.getName().substring(0, 1).toUpperCase(),
+ property.getName().substring(1));
+ final FieldSpec fieldSpec = FieldSpec
+ .builder(TypeName.INT, nameAllocator.newName(memberName), Modifier.PRIVATE)
+ .addJavadoc("Property ID of {@code $L}.\n", property.getName())
+ .build();
+
+ propertyIdFields.add(new PropertyIdField(fieldSpec, property));
+ }
+
+ return propertyIdFields;
+ }
+
+ /**
+ * Generate a method definition for
+ * {@link android.view.inspector.InspectionCompanion#getNodeName()}, if needed.
+ *
+ * If {@link InspectableClassModel#getNodeName()} is empty, This method returns an empty
+ * optional, otherwise, it generates a simple method that returns the string value of the
+ * node name.
+ *
+ * @param model The model to generate from
+ * @return The method definition or an empty Optional
+ */
+ private Optional<MethodSpec> generateGetNodeName(InspectableClassModel model) {
+ return model.getNodeName().map(nodeName -> MethodSpec.methodBuilder("getNodeName")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(String.class)
+ .addStatement("return $S", nodeName)
+ .build());
+ }
+
+ /**
+ * Generate a method definition for
+ * {@link android.view.inspector.InspectionCompanion#mapProperties(
+ * android.view.inspector.PropertyMapper)}.
+ *
+ * @param propertyIdFields A list of properties to map to ID fields
+ * @return The method definition
+ */
+ private MethodSpec generateMapProperties(List<PropertyIdField> propertyIdFields) {
+ final MethodSpec.Builder builder = MethodSpec.methodBuilder("mapProperties")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(PROPERTY_MAPPER, "propertyMapper");
+
+ propertyIdFields.forEach(p -> builder.addStatement(generatePropertyMapperInvocation(p)));
+ builder.addStatement("$N = true", M_PROPERTIES_MAPPED);
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a method definition for
+ * {@link android.view.inspector.InspectionCompanion#readProperties(
+ * Object, android.view.inspector.PropertyReader)}.
+ *
+ * @param model The model to generate from
+ * @param propertyIdFields A list of properties and ID fields to read from
+ * @return The method definition
+ */
+ private MethodSpec generateReadProperties(
+ InspectableClassModel model,
+ List<PropertyIdField> propertyIdFields) {
+ final MethodSpec.Builder builder = MethodSpec.methodBuilder("readProperties")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(model.getClassName(), "inspectable")
+ .addParameter(PROPERTY_READER, "propertyReader")
+ .addCode(generatePropertyMapInitializationCheck());
+
+ for (PropertyIdField propertyIdField : propertyIdFields) {
+ builder.addStatement(
+ "propertyReader.read$L($N, inspectable.$L())",
+ methodSuffixForPropertyType(propertyIdField.mProperty.getType()),
+ propertyIdField.mFieldSpec,
+ propertyIdField.mProperty.getGetter());
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a statement maps a property with a {@link android.view.inspector.PropertyMapper}.
+ *
+ * @param propertyIdField The property model and ID field to generate from
+ * @return A statement that invokes property mapper method
+ */
+ private CodeBlock generatePropertyMapperInvocation(PropertyIdField propertyIdField) {
+ final CodeBlock.Builder builder = CodeBlock.builder();
+ final Property property = propertyIdField.mProperty;
+ final FieldSpec fieldSpec = propertyIdField.mFieldSpec;
+
+ builder.add(
+ "$N = propertyMapper.map$L($S,$W",
+ fieldSpec,
+ methodSuffixForPropertyType(property.getType()),
+ property.getName());
+
+ if (property.isAttributeIdInferrableFromR()) {
+ builder.add("$T.attr.$L", R_CLASS_NAME, property.getName());
+ } else {
+ if (property.getAttributeId() == NO_ID) {
+ builder.add("$T.ID_NULL", RESOURCE_ID_CLASS_NAME);
+ } else {
+ builder.add("$L", String.format("0x%08x", property.getAttributeId()));
+ }
+ }
+
+ switch (property.getType()) {
+ case INT_ENUM:
+ throw new RuntimeException("IntEnumMapping generation not implemented");
+ case INT_FLAG:
+ throw new RuntimeException("IntFlagMapping generation not implemented");
+ default:
+ builder.add(")");
+ break;
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Generate a check that throws
+ * {@link android.view.inspector.InspectionCompanion.UninitializedPropertyMapException}
+ * if the properties haven't been initialized.
+ *
+ * <pre>
+ * if (!mPropertiesMapped) {
+ * throw new InspectionCompanion.UninitializedPropertyMapException();
+ * }
+ * </pre>
+ *
+ * @return A codeblock containing the property map initialization check
+ */
+ private CodeBlock generatePropertyMapInitializationCheck() {
+ return CodeBlock.builder()
+ .beginControlFlow("if (!$N)", M_PROPERTIES_MAPPED)
+ .addStatement(
+ "throw new $T()",
+ INSPECTION_COMPANION.nestedClass("UninitializedPropertyMapException"))
+ .endControlFlow()
+ .build();
+ }
+
+ /**
+ * Generate the final class name for the inspection companion from the model's class name.
+ *
+ * The generated class is added to the same package as the source class. If the class in the
+ * model is a nested class, the nested class names are joined with {@code "$"}. The suffix
+ * {@code "$$InspectionCompanion"} is always added the the generated name. E.g.: For modeled
+ * class {@code com.example.Outer.Inner}, the generated class name will be
+ * {@code com.example.Outer$Inner$$InspectionCompanion}.
+ *
+ * @param model The model to generate from
+ * @return A class name for the generated inspection companion class
+ */
+ private static ClassName generateClassName(InspectableClassModel model) {
+ final ClassName className = model.getClassName();
+
+ return ClassName.get(
+ className.packageName(),
+ String.join("$", className.simpleNames()) + GENERATED_CLASS_SUFFIX);
+ }
+
+ /**
+ * Get the suffix for a {@code map} or {@code read} method for a property type.
+ *
+ * @param type The requested property type
+ * @return A method suffix
+ */
+ private static String methodSuffixForPropertyType(Property.Type type) {
+ switch (type) {
+ case BOOLEAN:
+ return "Boolean";
+ case BYTE:
+ return "Byte";
+ case CHAR:
+ return "Char";
+ case DOUBLE:
+ return "Double";
+ case FLOAT:
+ return "Float";
+ case INT:
+ return "Int";
+ case LONG:
+ return "Long";
+ case SHORT:
+ return "Short";
+ case OBJECT:
+ return "Object";
+ case COLOR:
+ return "Color";
+ case GRAVITY:
+ return "Gravity";
+ case INT_ENUM:
+ return "IntEnum";
+ case INT_FLAG:
+ return "IntFlag";
+ default:
+ throw new NoSuchElementException(String.format("No such property type, %s", type));
+ }
+ }
+
+ /**
+ * Value class that holds a {@link Property} and a {@link FieldSpec} for that property.
+ */
+ private static final class PropertyIdField {
+ private final FieldSpec mFieldSpec;
+ private final Property mProperty;
+
+ private PropertyIdField(FieldSpec fieldSpec, Property property) {
+ mFieldSpec = fieldSpec;
+ mProperty = property;
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
new file mode 100644
index 000000000000..3ffcff8a87d3
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import javax.lang.model.element.Element;
+
+/**
+ * An interface for annotation processors that operate on a single element and a class model.
+ */
+public interface ModelProcessor {
+ /**
+ * Process the supplied element, mutating the model as needed.
+ *
+ * @param element The annotated element to operate on
+ * @param model The model this element should be merged into
+ */
+ void process(Element element, InspectableClassModel model);
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
new file mode 100644
index 000000000000..e531b67d9ea2
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.squareup.javapoet.ClassName;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+
+
+/**
+ * An annotation processor for the platform inspectable annotations.
+ *
+ * It mostly delegates to {@link ModelProcessor} and {@link InspectionCompanionGenerator}. This
+ * modular architecture allows the core generation code to be reused for comparable annotations
+ * outside the platform, such as in AndroidX.
+ *
+ * @see android.view.inspector.InspectableNodeName
+ * @see android.view.inspector.InspectableProperty
+ */
+@SupportedAnnotationTypes({
+ PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME
+})
+public final class PlatformInspectableProcessor extends AbstractProcessor {
+ static final String NODE_NAME_QUALIFIED_NAME =
+ "android.view.inspector.InspectableNodeName";
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ final Map<String, InspectableClassModel> modelMap = new HashMap<>();
+
+ for (TypeElement annotation : annotations) {
+ if (annotation.getQualifiedName().contentEquals(NODE_NAME_QUALIFIED_NAME)) {
+ runModelProcessor(
+ roundEnv.getElementsAnnotatedWith(annotation),
+ new InspectableNodeNameProcessor(NODE_NAME_QUALIFIED_NAME, processingEnv),
+ modelMap);
+
+
+ } else {
+ fail("Unexpected annotation type", annotation);
+ }
+ }
+
+ final InspectionCompanionGenerator generator =
+ new InspectionCompanionGenerator(processingEnv.getFiler(), getClass());
+
+ for (InspectableClassModel model : modelMap.values()) {
+ try {
+ generator.generate(model);
+ } catch (IOException ioException) {
+ fail(String.format(
+ "Unable to generate inspection companion for %s due to %s",
+ model.getClassName().toString(),
+ ioException.getMessage()));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Run a {@link ModelProcessor} for a set of elements
+ *
+ * @param elements Elements to process, should be annotated correctly
+ * @param processor The processor to use
+ * @param modelMap A map of qualified class names to models
+ */
+ private void runModelProcessor(
+ Set<? extends Element> elements,
+ ModelProcessor processor,
+ Map<String, InspectableClassModel> modelMap) {
+ for (Element element : elements) {
+ final Optional<TypeElement> classElement = enclosingClassElement(element);
+
+ if (!classElement.isPresent()) {
+ fail("Element not contained in a class", element);
+ break;
+ }
+
+ final InspectableClassModel model = modelMap.computeIfAbsent(
+ classElement.get().getQualifiedName().toString(),
+ k -> new InspectableClassModel(ClassName.get(classElement.get())));
+
+ processor.process(element, model);
+ }
+ }
+
+ /**
+ * Get the nearest enclosing class if there is one.
+ *
+ * If {@param element} represents a class, it will be returned wrapped in an optional.
+ *
+ * @param element An element to search from
+ * @return A TypeElement of the nearest enclosing class or an empty optional
+ */
+ private static Optional<TypeElement> enclosingClassElement(Element element) {
+ Element cursor = element;
+
+ while (cursor != null) {
+ if (cursor.getKind() == ElementKind.CLASS) {
+ return Optional.of((TypeElement) cursor);
+ }
+
+ cursor = cursor.getEnclosingElement();
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Print message and fail the build.
+ *
+ * @param message Message to print
+ */
+ private void fail(String message) {
+ processingEnv.getMessager().printMessage(ERROR, message);
+ }
+
+ /**
+ * Print message and fail the build.
+ *
+ * @param message Message to print
+ * @param element The element that failed
+ */
+ private void fail(String message, Element element) {
+ processingEnv.getMessager().printMessage(ERROR, message, element);
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
new file mode 100644
index 000000000000..6360e0a2de39
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+
+/**
+ * Internal exception used to signal an error processing an annotation.
+ */
+final class ProcessingException extends RuntimeException {
+ private final Element mElement;
+ private final AnnotationMirror mAnnotationMirror;
+ private final AnnotationValue mAnnotationValue;
+
+ ProcessingException(String message) {
+ this(message, null, null, null);
+ }
+
+ ProcessingException(String message, Element element) {
+ this(message, element, null, null);
+ }
+
+ ProcessingException(String message, Element element, AnnotationMirror annotationMirror) {
+ this(message, element, annotationMirror, null);
+ }
+
+ ProcessingException(
+ String message,
+ Element element,
+ AnnotationMirror annotationMirror,
+ AnnotationValue annotationValue) {
+ super(message);
+ mElement = element;
+ mAnnotationMirror = annotationMirror;
+ mAnnotationValue = annotationValue;
+ }
+
+ /**
+ * Prints the exception to a Messager.
+ *
+ * @param messager A Messager to print to
+ */
+ void print(Messager messager) {
+ if (mElement != null) {
+ if (mAnnotationMirror != null) {
+ if (mAnnotationValue != null) {
+ messager.printMessage(
+ ERROR, getMessage(), mElement, mAnnotationMirror, mAnnotationValue);
+ } else {
+ messager.printMessage(ERROR, getMessage(), mElement, mAnnotationMirror);
+ }
+ } else {
+ messager.printMessage(ERROR, getMessage(), mElement);
+ }
+ } else {
+ messager.printMessage(ERROR, getMessage());
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 000000000000..fa4f71ffd0fa
--- /dev/null
+++ b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.processor.inspector.view.PlatformInspectableProcessor
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
new file mode 100644
index 000000000000..f639719800f6
--- /dev/null
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import android.processor.view.inspector.InspectableClassModel.Property;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.fail;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.squareup.javapoet.ClassName;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Optional;
+
+/**
+ * Tests for {@link InspectionCompanionGenerator}
+ */
+public class InspectionCompanionGeneratorTest {
+ private static final String RESOURCE_PATH_TEMPLATE =
+ "android/processor/view/inspector/InspectionCompanionGeneratorTest/%s.java.txt";
+ private static final ClassName TEST_CLASS_NAME =
+ ClassName.get("com.android.inspectable", "TestInspectable");
+ private InspectableClassModel mModel;
+ private InspectionCompanionGenerator mGenerator;
+
+ @Before
+ public void setup() {
+ mModel = new InspectableClassModel(TEST_CLASS_NAME);
+ mGenerator = new InspectionCompanionGenerator(null, getClass());
+ }
+
+ @Test
+ public void testNodeName() {
+ mModel.setNodeName(Optional.of("NodeName"));
+ assertGeneratedFileEquals("NodeName");
+ }
+
+ @Test
+ public void testNestedClass() {
+ mModel = new InspectableClassModel(
+ ClassName.get("com.android.inspectable", "Outer", "Inner"));
+ assertGeneratedFileEquals("NestedClass");
+ }
+
+ @Test
+ public void testSimpleProperties() {
+ addProperty("boolean", Property.Type.BOOLEAN, "getBoolean");
+ addProperty("byte", Property.Type.BYTE, "getByte");
+ addProperty("char", Property.Type.CHAR, "getChar");
+ addProperty("double", Property.Type.DOUBLE, "getDouble");
+ addProperty("float", Property.Type.FLOAT, "getFloat");
+ addProperty("int", Property.Type.INT, "getInt");
+ addProperty("long", Property.Type.LONG, "getLong");
+ addProperty("short", Property.Type.SHORT, "getShort");
+
+ addProperty("object", Property.Type.OBJECT, "getObject");
+ addProperty("color", Property.Type.COLOR, "getColor");
+ addProperty("gravity", Property.Type.GRAVITY, "getGravity");
+
+ assertGeneratedFileEquals("SimpleProperties");
+ }
+
+ @Test
+ public void testNoAttributeId() {
+ final Property property = new Property("noAttributeProperty");
+ property.setType(Property.Type.INT);
+ property.setGetter("getNoAttributeProperty");
+ property.setAttributeIdInferrableFromR(false);
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("NoAttributeId");
+ }
+
+ @Test
+ public void testSuppliedAttributeId() {
+ final Property property = new Property("suppliedAttributeProperty");
+ property.setType(Property.Type.INT);
+ property.setGetter("getSuppliedAttributeProperty");
+ property.setAttributeId(0xdecafbad);
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("SuppliedAttributeId");
+ }
+
+ private Property addProperty(String name, Property.Type type, String getter) {
+ final Property property = new Property(name);
+ property.setType(type);
+ property.setGetter(getter);
+ mModel.putProperty(property);
+ return property;
+ }
+
+ private void assertGeneratedFileEquals(String fileName) {
+ assertEquals(
+ loadTextResource(String.format(RESOURCE_PATH_TEMPLATE, fileName)),
+ mGenerator.generateFile(mModel).toString());
+ }
+
+ private String loadTextResource(String path) {
+ try {
+ final URL url = Resources.getResource(path);
+ assertNotNull(String.format("Resource file not found: %s", path), url);
+ return Resources.toString(url, Charsets.UTF_8);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ return null;
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
new file mode 100644
index 000000000000..2fc242c6cf4c
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
@@ -0,0 +1,31 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link Outer.Inner}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class Outer$Inner$$InspectionCompanion implements InspectionCompanion<Outer.Inner> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
new file mode 100644
index 000000000000..277e84065fb7
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
@@ -0,0 +1,39 @@
+package com.android.inspectable;
+
+import android.content.res.ResourceId;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code noAttributeProperty}.
+ */
+ private int mNoAttributePropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mNoAttributePropertyId = propertyMapper.mapInt("noAttributeProperty", ResourceId.ID_NULL);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readInt(mNoAttributePropertyId, inspectable.getNoAttributeProperty());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
new file mode 100644
index 000000000000..11425482ce94
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
@@ -0,0 +1,37 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+import java.lang.String;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ }
+
+ @Override
+ public String getNodeName() {
+ return "NodeName";
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
new file mode 100644
index 000000000000..57eb08041131
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
@@ -0,0 +1,109 @@
+package com.android.inspectable;
+
+import android.R;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code boolean}.
+ */
+ private int mBooleanId;
+
+ /**
+ * Property ID of {@code byte}.
+ */
+ private int mByteId;
+
+ /**
+ * Property ID of {@code char}.
+ */
+ private int mCharId;
+
+ /**
+ * Property ID of {@code color}.
+ */
+ private int mColorId;
+
+ /**
+ * Property ID of {@code double}.
+ */
+ private int mDoubleId;
+
+ /**
+ * Property ID of {@code float}.
+ */
+ private int mFloatId;
+
+ /**
+ * Property ID of {@code gravity}.
+ */
+ private int mGravityId;
+
+ /**
+ * Property ID of {@code int}.
+ */
+ private int mIntId;
+
+ /**
+ * Property ID of {@code long}.
+ */
+ private int mLongId;
+
+ /**
+ * Property ID of {@code object}.
+ */
+ private int mObjectId;
+
+ /**
+ * Property ID of {@code short}.
+ */
+ private int mShortId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mBooleanId = propertyMapper.mapBoolean("boolean", R.attr.boolean);
+ mByteId = propertyMapper.mapByte("byte", R.attr.byte);
+ mCharId = propertyMapper.mapChar("char", R.attr.char);
+ mColorId = propertyMapper.mapColor("color", R.attr.color);
+ mDoubleId = propertyMapper.mapDouble("double", R.attr.double);
+ mFloatId = propertyMapper.mapFloat("float", R.attr.float);
+ mGravityId = propertyMapper.mapGravity("gravity", R.attr.gravity);
+ mIntId = propertyMapper.mapInt("int", R.attr.int);
+ mLongId = propertyMapper.mapLong("long", R.attr.long);
+ mObjectId = propertyMapper.mapObject("object", R.attr.object);
+ mShortId = propertyMapper.mapShort("short", R.attr.short);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readBoolean(mBooleanId, inspectable.getBoolean());
+ propertyReader.readByte(mByteId, inspectable.getByte());
+ propertyReader.readChar(mCharId, inspectable.getChar());
+ propertyReader.readColor(mColorId, inspectable.getColor());
+ propertyReader.readDouble(mDoubleId, inspectable.getDouble());
+ propertyReader.readFloat(mFloatId, inspectable.getFloat());
+ propertyReader.readGravity(mGravityId, inspectable.getGravity());
+ propertyReader.readInt(mIntId, inspectable.getInt());
+ propertyReader.readLong(mLongId, inspectable.getLong());
+ propertyReader.readObject(mObjectId, inspectable.getObject());
+ propertyReader.readShort(mShortId, inspectable.getShort());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
new file mode 100644
index 000000000000..6b6ce2157481
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
@@ -0,0 +1,39 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code suppliedAttributeProperty}.
+ */
+ private int mSuppliedAttributePropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mSuppliedAttributePropertyId = propertyMapper.mapInt("suppliedAttributeProperty",
+ 0xdecafbad);
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readInt(mSuppliedAttributePropertyId, inspectable.getSuppliedAttributeProperty());
+ }
+}
diff --git a/tools/signedconfig/debug_key.pem b/tools/signedconfig/debug_key.pem
new file mode 100644
index 000000000000..0af577bf81e1
--- /dev/null
+++ b/tools/signedconfig/debug_key.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEfgtO+KPOoqJqTnqkDDKkAcOzyvtovsUO/ShLE6y4XRoAoGCCqGSM49
+AwEHoUQDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60pj1pnU8
+SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+-----END EC PRIVATE KEY-----
diff --git a/tools/signedconfig/debug_public.pem b/tools/signedconfig/debug_public.pem
new file mode 100644
index 000000000000..f61f81322b94
--- /dev/null
+++ b/tools/signedconfig/debug_public.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoE
+CGbTEBTKKvdd2hO60pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
+-----END PUBLIC KEY-----
diff --git a/tools/signedconfig/debug_sign.sh b/tools/signedconfig/debug_sign.sh
new file mode 100755
index 000000000000..28e54289f8f8
--- /dev/null
+++ b/tools/signedconfig/debug_sign.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# Script to sign data with the debug keys. Outputs base64 for embedding into
+# APK metadata.
+
+openssl dgst -sha256 -sign $(dirname $0)/debug_key.pem $1 | base64 -w 0
+echo
diff --git a/tools/signedconfig/gen_priv_key.sh b/tools/signedconfig/gen_priv_key.sh
new file mode 100755
index 000000000000..834c86bc8c12
--- /dev/null
+++ b/tools/signedconfig/gen_priv_key.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# This script acts as a record of how the debug key was generated. There should
+# be no need to run it again.
+
+openssl ecparam -name prime256v1 -genkey -noout -out debug_key.pem
+openssl ec -in debug_key.pem -pubout -out debug_public.pem
diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh
new file mode 100755
index 000000000000..8e1f58ce7b45
--- /dev/null
+++ b/tools/signedconfig/verify_b64.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Script to verify signatures, with both signature & data given in b64
+# Args:
+# 1. data (base64 encoded)
+# 2. signature (base64 encoded)
+# The arg values can be taken from the debug log for SignedConfigService when verbose logging is
+# enabled.
+
+openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d)
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 88b7e2e9de21..5192a0e7bf19 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -264,6 +264,10 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
+
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
"const std::map<int, int64_t>& arg%d_2, "
@@ -343,6 +347,10 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
fprintf(out, " }\n");
fprintf(out, " event.end();\n\n");
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out,
+ " event.AppendCharArray(arg%d, arg%d_length);\n",
+ argIndex, argIndex);
} else {
if (*arg == JAVA_TYPE_STRING) {
fprintf(out, " if (arg%d == NULL) {\n", argIndex);
@@ -383,12 +391,17 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
+
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
- "const std::map<int, int64_t>& arg%d_2, "
- "const std::map<int, char const*>& arg%d_3, "
- "const std::map<int, float>& arg%d_4",
- argIndex, argIndex, argIndex, argIndex);
+ fprintf(out,
+ ", const std::map<int, int32_t>& arg%d_1, "
+ "const std::map<int, int64_t>& arg%d_2, "
+ "const std::map<int, char const*>& arg%d_3, "
+ "const std::map<int, float>& arg%d_4",
+ argIndex, argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -415,9 +428,11 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
chainField.name.c_str(), chainField.name.c_str());
}
}
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4",
- argIndex, argIndex, argIndex, argIndex);
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", arg%d, arg%d_length", argIndex, argIndex);
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
+ argIndex, argIndex, argIndex);
} else {
fprintf(out, ", arg%d", argIndex);
}
@@ -580,6 +595,11 @@ static void write_cpp_usage(
field->name.c_str(),
field->name.c_str(),
field->name.c_str());
+ } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s %s, size_t %s_length",
+ cpp_type_name(field->javaType), field->name.c_str(),
+ field->name.c_str());
+
} else {
fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
}
@@ -613,6 +633,9 @@ static void write_cpp_method_header(
"const std::map<int, char const*>& arg%d_3, "
"const std::map<int, float>& arg%d_4",
argIndex, argIndex, argIndex, argIndex);
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -1128,6 +1151,7 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp
hadStringOrChain = true;
fprintf(out, " jbyte* jbyte_array%d;\n", argIndex);
fprintf(out, " const char* str%d;\n", argIndex);
+ fprintf(out, " int str%d_length = 0;\n", argIndex);
fprintf(out,
" if (arg%d != NULL && env->GetArrayLength(arg%d) > "
"0) {\n",
@@ -1137,6 +1161,9 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp
"env->GetByteArrayElements(arg%d, NULL);\n",
argIndex, argIndex);
fprintf(out,
+ " str%d_length = env->GetArrayLength(arg%d);\n",
+ argIndex, argIndex);
+ fprintf(out,
" str%d = "
"reinterpret_cast<char*>(env->GetByteArrayElements(arg%"
"d, NULL));\n",
@@ -1224,6 +1251,10 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp
? "str"
: "arg";
fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+
+ if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s%d_length", argName, argIndex);
+ }
}
argIndex++;
}