summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.prebuilt_info/OWNERS1
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb4
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb4
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb4
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb4
-rw-r--r--Android.bp7
-rw-r--r--StubLibraries.bp5
-rw-r--r--TEST_MAPPING62
-rw-r--r--apex/appsearch/framework/api/current.txt78
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java438
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java1
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java59
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java27
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java26
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java26
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java249
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java16
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java22
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java2
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java4
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java61
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java315
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java7
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java2
-rw-r--r--apex/jobscheduler/framework/java/android/app/AlarmManager.java12
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java8
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java13
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java4
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobService.java7
-rw-r--r--apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java4
-rw-r--r--apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl2
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java649
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java359
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java81
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java11
-rw-r--r--apex/media/framework/Android.bp4
-rw-r--r--apex/media/framework/api/system-current.txt38
-rw-r--r--apex/media/framework/java/android/media/ApplicationMediaCapabilities.java4
-rw-r--r--apex/media/framework/java/android/media/MediaFeature.java4
-rw-r--r--apex/media/framework/java/android/media/MediaFrameworkInitializer.java3
-rw-r--r--apex/media/framework/java/android/media/MediaTranscodeManager.java725
-rw-r--r--api/Android.bp48
-rw-r--r--cmds/app_process/Android.bp13
-rw-r--r--cmds/app_process/version-script32.txt15
-rw-r--r--cmds/app_process/version-script64.txt14
-rw-r--r--cmds/sm/src/com/android/commands/sm/Sm.java2
-rw-r--r--config/preloaded-classes2
-rw-r--r--core/api/current.txt2733
-rw-r--r--core/api/module-lib-current.txt49
-rw-r--r--core/api/system-current.txt356
-rw-r--r--core/api/test-current.txt67
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java5
-rw-r--r--core/java/android/app/Activity.java12
-rw-r--r--core/java/android/app/ActivityClient.java8
-rw-r--r--core/java/android/app/ActivityManager.java38
-rw-r--r--core/java/android/app/ActivityTaskManager.java14
-rw-r--r--core/java/android/app/ActivityThread.java64
-rw-r--r--core/java/android/app/AppOpsManager.java102
-rw-r--r--core/java/android/app/ApplicationPackageManager.java19
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/app/IActivityClientController.aidl5
-rw-r--r--core/java/android/app/IActivityManager.aidl3
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl7
-rw-r--r--core/java/android/app/IWallpaperManager.aidl3
-rw-r--r--core/java/android/app/LoadedApk.java4
-rw-r--r--core/java/android/app/Notification.java375
-rw-r--r--core/java/android/app/StatusBarManager.java6
-rw-r--r--core/java/android/app/SystemServiceRegistry.java11
-rw-r--r--core/java/android/app/WallpaperManager.java97
-rw-r--r--core/java/android/app/WindowTokenClient.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java25
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java2
-rw-r--r--core/java/android/app/time/Capabilities.java78
-rw-r--r--core/java/android/app/time/TimeCapabilities.aidl19
-rw-r--r--core/java/android/app/time/TimeCapabilities.java186
-rw-r--r--core/java/android/app/time/TimeCapabilitiesAndConfig.aidl19
-rw-r--r--core/java/android/app/time/TimeCapabilitiesAndConfig.java119
-rw-r--r--core/java/android/app/time/TimeConfiguration.aidl19
-rw-r--r--core/java/android/app/time/TimeConfiguration.java141
-rw-r--r--core/java/android/app/time/TimeManager.java48
-rw-r--r--core/java/android/app/time/TimeZoneCapabilities.java55
-rw-r--r--core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java2
-rw-r--r--core/java/android/app/timedetector/ITimeDetectorService.aidl5
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java15
-rw-r--r--core/java/android/app/usage/UsageStats.java25
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java6
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java2
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java52
-rw-r--r--core/java/android/bluetooth/OobData.java982
-rw-r--r--core/java/android/content/ClipData.java29
-rw-r--r--core/java/android/content/ClipDescription.java110
-rw-r--r--core/java/android/content/ContentProvider.java42
-rw-r--r--core/java/android/content/Context.java44
-rw-r--r--core/java/android/content/Intent.java46
-rw-r--r--core/java/android/content/SyncAdapterType.java2
-rw-r--r--core/java/android/content/pm/ActivityInfo.java203
-rw-r--r--core/java/android/content/pm/IDataLoaderStatusListener.aidl18
-rw-r--r--core/java/android/content/pm/PackageParser.java8
-rw-r--r--core/java/android/content/pm/ResolveInfo.java33
-rw-r--r--core/java/android/content/pm/dex/DexMetadataHelper.java4
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java5
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivity.java12
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivityUtils.java5
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationInfo.java122
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationManager.java273
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationState.java135
-rw-r--r--core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl4
-rw-r--r--core/java/android/ddm/DdmHandleHeap.java183
-rw-r--r--core/java/android/ddm/DdmHandleThread.java181
-rw-r--r--core/java/android/ddm/DdmRegister.java4
-rw-r--r--core/java/android/graphics/fonts/FontFamilyUpdateRequest.java46
-rw-r--r--core/java/android/graphics/fonts/FontManager.java1
-rw-r--r--core/java/android/hardware/SensorPrivacyManagerInternal.java64
-rw-r--r--core/java/android/hardware/SystemSensorManager.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java31
-rw-r--r--core/java/android/hardware/biometrics/IAuthService.aidl4
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl4
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl4
-rw-r--r--core/java/android/hardware/biometrics/SensorPropertiesInternal.java14
-rw-r--r--core/java/android/hardware/camera2/MultiResolutionImageReader.java10
-rw-r--r--core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java20
-rw-r--r--core/java/android/hardware/display/AmbientDisplayConfiguration.java15
-rw-r--r--core/java/android/hardware/display/BrightnessChangeEvent.java31
-rw-r--r--core/java/android/hardware/display/DisplayManager.java3
-rw-r--r--core/java/android/hardware/face/FaceManager.java23
-rw-r--r--core/java/android/hardware/face/FaceSensorPropertiesInternal.java7
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java11
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java20
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java15
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java11
-rw-r--r--core/java/android/net/INetworkPolicyListener.aidl1
-rw-r--r--core/java/android/net/IOnCompleteListener.aidl (renamed from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl)2
-rw-r--r--core/java/android/net/IPacProxyInstalledListener.aidl25
-rw-r--r--core/java/android/net/IPacProxyManager.aidl28
-rw-r--r--core/java/android/net/NetworkPolicyManager.java294
-rw-r--r--core/java/android/net/NetworkScore.aidl (renamed from core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml)15
-rw-r--r--core/java/android/net/NetworkScore.java108
-rw-r--r--core/java/android/net/PacProxyManager.java150
-rw-r--r--core/java/android/net/PacProxySelector.java2
-rw-r--r--core/java/android/net/VpnManager.java7
-rw-r--r--core/java/android/net/vcn/VcnManager.java65
-rw-r--r--core/java/android/os/BatteryManagerInternal.java25
-rw-r--r--core/java/android/os/BatteryStats.java58
-rw-r--r--core/java/android/os/BatteryStatsManager.java73
-rwxr-xr-xcore/java/android/os/Build.java16
-rw-r--r--core/java/android/os/Environment.java60
-rw-r--r--core/java/android/os/FileUtils.java20
-rw-r--r--core/java/android/os/PowerManager.java17
-rw-r--r--core/java/android/os/Process.java4
-rw-r--r--core/java/android/os/RemoteCallbackList.java18
-rw-r--r--core/java/android/os/Vibrator.java22
-rw-r--r--core/java/android/os/VibratorInfo.java39
-rw-r--r--core/java/android/os/connectivity/WifiActivityEnergyInfo.java4
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java7
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java21
-rw-r--r--core/java/android/os/incremental/IncrementalMetrics.java39
-rw-r--r--core/java/android/os/incremental/IncrementalStorage.java14
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java5
-rw-r--r--core/java/android/permission/PermissionManager.java22
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java539
-rw-r--r--core/java/android/provider/Settings.java67
-rw-r--r--core/java/android/provider/Telephony.java7
-rw-r--r--core/java/android/service/autofill/AutofillServiceInfo.java35
-rw-r--r--core/java/android/service/autofill/Dataset.java151
-rw-r--r--core/java/android/service/autofill/FillResponse.java33
-rw-r--r--core/java/android/service/autofill/InlinePresentation.java22
-rw-r--r--core/java/android/service/displayhash/DisplayHashParams.aidl19
-rw-r--r--core/java/android/service/displayhash/DisplayHashParams.java251
-rw-r--r--core/java/android/service/displayhash/DisplayHasherService.java34
-rw-r--r--core/java/android/service/displayhash/IDisplayHasherService.aidl7
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java3
-rw-r--r--core/java/android/service/rotationresolver/RotationResolutionRequest.java216
-rw-r--r--core/java/android/service/timezone/TimeZoneProviderService.java28
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java60
-rw-r--r--core/java/android/service/voice/HotwordDetectionService.java34
-rw-r--r--core/java/android/service/voice/IHotwordDetectionService.aidl4
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java110
-rw-r--r--core/java/android/speech/IRecognitionServiceManager.aidl2
-rw-r--r--core/java/android/speech/SpeechRecognizer.java121
-rw-r--r--core/java/android/telephony/PhoneStateListener.java10
-rw-r--r--core/java/android/telephony/TelephonyCallback.java91
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java36
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/Slog.java100
-rw-r--r--core/java/android/util/apk/ApkSigningBlockUtils.java5
-rw-r--r--core/java/android/util/apk/DataSource.java22
-rw-r--r--core/java/android/util/apk/MemoryMappedFileDataSource.java1
-rw-r--r--core/java/android/util/apk/ReadFileDataSource.java73
-rw-r--r--core/java/android/util/apk/TEST_MAPPING11
-rw-r--r--core/java/android/util/apk/VerityBuilder.java8
-rw-r--r--core/java/android/util/imetracing/ImeTracing.java7
-rw-r--r--core/java/android/util/imetracing/ImeTracingClientImpl.java6
-rw-r--r--core/java/android/util/imetracing/ImeTracingServerImpl.java27
-rw-r--r--core/java/android/uwb/AngleMeasurement.java107
-rw-r--r--core/java/android/uwb/AngleOfArrivalMeasurement.java21
-rw-r--r--core/java/android/uwb/IUwbAdapter.aidl13
-rw-r--r--core/java/android/uwb/RangingManager.java33
-rw-r--r--core/java/android/uwb/UwbManager.java5
-rw-r--r--core/java/android/view/CrossWindowBlurListeners.java37
-rw-r--r--core/java/android/view/Display.java162
-rw-r--r--core/java/android/view/DisplayInfo.java21
-rw-r--r--core/java/android/view/DragAndDropPermissions.java59
-rw-r--r--core/java/android/view/IRecentsAnimationController.aidl15
-rw-r--r--core/java/android/view/IScrollCaptureCallbacks.aidl7
-rw-r--r--core/java/android/view/IScrollCaptureConnection.aidl8
-rw-r--r--core/java/android/view/IScrollCaptureResponseListener.aidl35
-rw-r--r--core/java/android/view/IWindow.aidl4
-rw-r--r--core/java/android/view/IWindowManager.aidl8
-rw-r--r--core/java/android/view/InputEventReceiver.java10
-rw-r--r--core/java/android/view/KeyCharacterMap.java29
-rw-r--r--core/java/android/view/OWNERS5
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java23
-rw-r--r--core/java/android/view/RoundedCorner.java2
-rw-r--r--core/java/android/view/RoundedCorners.java8
-rw-r--r--core/java/android/view/ScrollCaptureConnection.java115
-rw-r--r--core/java/android/view/ScrollCaptureResponse.java20
-rw-r--r--core/java/android/view/SurfaceControl.java52
-rw-r--r--core/java/android/view/SurfaceView.java14
-rw-r--r--core/java/android/view/ViewRootImpl.java39
-rw-r--r--core/java/android/view/Window.java4
-rw-r--r--core/java/android/view/WindowManager.java43
-rw-r--r--core/java/android/view/WindowManagerImpl.java19
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java2
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestion.java121
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionInfo.java56
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java160
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java23
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java108
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java4
-rw-r--r--core/java/android/view/textservice/SpellCheckerSession.java11
-rw-r--r--core/java/android/view/textservice/TextServicesManager.java19
-rw-r--r--core/java/android/view/translation/ITranslationManager.aidl4
-rw-r--r--core/java/android/view/translation/UiTranslationController.java84
-rw-r--r--core/java/android/view/translation/UiTranslationManager.java155
-rw-r--r--core/java/android/view/translation/UiTranslationStateCallback.java48
-rw-r--r--core/java/android/widget/AnalogClock.java2
-rw-r--r--core/java/android/widget/EdgeEffect.java198
-rw-r--r--core/java/android/widget/Editor.java13
-rw-r--r--core/java/android/widget/OWNERS2
-rw-r--r--core/java/android/widget/RemoteCollectionItemsAdapter.java217
-rw-r--r--core/java/android/widget/RemoteViews.java639
-rw-r--r--core/java/android/widget/SpellChecker.java249
-rw-r--r--core/java/android/widget/TextView.java34
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl7
-rw-r--r--core/java/android/window/SizeConfigurationBuckets.aidl19
-rw-r--r--core/java/android/window/SizeConfigurationBuckets.java282
-rw-r--r--core/java/android/window/SplashScreenView.java19
-rw-r--r--core/java/android/window/StartingWindowInfo.java8
-rw-r--r--core/java/android/window/TaskOrganizer.java15
-rw-r--r--core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java34
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java2
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl11
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl22
-rw-r--r--core/java/com/android/internal/compat/AndroidBuildClassifier.java10
-rw-r--r--core/java/com/android/internal/compat/OverrideAllowedState.java14
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java6
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java13
-rw-r--r--core/java/com/android/internal/graphics/palette/Mean.java9
-rw-r--r--core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java23
-rw-r--r--core/java/com/android/internal/graphics/palette/WuQuantizer.java15
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java5
-rw-r--r--core/java/com/android/internal/notification/SystemNotificationChannels.java7
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java274
-rw-r--r--core/java/com/android/internal/os/BluetoothPowerCalculator.java47
-rw-r--r--core/java/com/android/internal/os/CpuPowerCalculator.java98
-rw-r--r--core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java57
-rw-r--r--core/java/com/android/internal/os/TEST_MAPPING6
-rw-r--r--core/java/com/android/internal/os/WifiPowerCalculator.java129
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java14
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java8
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java6
-rw-r--r--core/java/com/android/internal/protolog/BaseProtoLogImpl.java12
-rw-r--r--core/java/com/android/internal/security/OWNERS3
-rw-r--r--core/java/com/android/internal/security/TEST_MAPPING (renamed from services/core/java/com/android/server/security/TEST_MAPPING)0
-rw-r--r--core/java/com/android/internal/security/VerityUtils.java (renamed from services/core/java/com/android/server/security/VerityUtils.java)14
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl8
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl4
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl5
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java2
-rw-r--r--core/java/com/android/internal/util/LocationPermissionChecker.java303
-rw-r--r--core/java/com/android/internal/util/Protocol.java2
-rw-r--r--core/java/com/android/internal/util/ScreenRecordHelper.java49
-rw-r--r--core/java/com/android/internal/util/TrafficStatsConstants.java16
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java6
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java96
-rw-r--r--core/java/com/android/internal/view/InputBindResult.java11
-rw-r--r--core/java/com/android/internal/view/OWNERS4
-rw-r--r--core/java/com/android/internal/view/inline/InlineTooltipUi.java337
-rw-r--r--core/java/com/android/internal/view/inline/OWNERS1
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java1
-rw-r--r--core/java/com/android/internal/widget/ImageFloatingTextView.java67
-rw-r--r--core/java/com/android/internal/widget/OWNERS2
-rw-r--r--core/java/com/android/internal/widget/RecyclerView.java144
-rw-r--r--core/jni/Android.bp7
-rw-r--r--core/jni/AndroidRuntime.cpp26
-rw-r--r--core/jni/android_media_AudioRecord.cpp25
-rw-r--r--core/jni/android_media_AudioSystem.cpp68
-rw-r--r--core/jni/android_media_AudioTrack.cpp14
-rw-r--r--core/jni/android_net_NetworkUtils.cpp39
-rw-r--r--core/jni/android_os_SharedMemory.cpp17
-rw-r--r--core/jni/android_os_incremental_IncrementalManager.cpp5
-rw-r--r--core/jni/android_util_Binder.cpp4
-rw-r--r--core/jni/android_util_Process.cpp20
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp106
-rw-r--r--core/jni/android_view_InputEventSender.cpp40
-rw-r--r--core/jni/android_view_KeyCharacterMap.cpp55
-rw-r--r--core/jni/android_view_SurfaceControl.cpp12
-rw-r--r--core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp8
-rw-r--r--core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp4
-rw-r--r--core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp20
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp42
-rw-r--r--core/jni/com_android_internal_security_VerityUtils.cpp (renamed from services/core/jni/com_android_server_security_VerityUtils.cpp)14
-rw-r--r--core/jni/permission_utils.cpp71
-rw-r--r--core/jni/permission_utils.h (renamed from packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java)22
-rw-r--r--core/proto/OWNERS3
-rw-r--r--core/proto/android/app/OWNERS2
-rw-r--r--core/proto/android/app/location_time_zone_manager.proto10
-rw-r--r--core/proto/android/app/time_zone_detector.proto59
-rw-r--r--core/proto/android/os/incident.proto6
-rw-r--r--core/proto/android/providers/settings/secure.proto8
-rw-r--r--core/proto/android/server/biometrics.proto7
-rw-r--r--core/proto/android/server/usagestatsservice.proto2
-rw-r--r--core/proto/android/server/usagestatsservice_v2.proto1
-rw-r--r--core/proto/android/server/windowmanagerservice.proto3
-rw-r--r--core/res/AndroidManifest.xml50
-rw-r--r--core/res/res/drawable/ic_accessibility_24dp.xml27
-rw-r--r--core/res/res/layout/notification_expand_button.xml2
-rw-r--r--core/res/res/layout/notification_template_material_big_base.xml2
-rw-r--r--core/res/res/layout/notification_template_material_big_call.xml107
-rw-r--r--core/res/res/layout/notification_template_material_big_text.xml2
-rw-r--r--core/res/res/layout/notification_template_material_call.xml60
-rw-r--r--core/res/res/layout/notification_template_text_multiline.xml2
-rw-r--r--core/res/res/values-af/strings.xml33
-rw-r--r--core/res/res/values-am/strings.xml33
-rw-r--r--core/res/res/values-ar/strings.xml35
-rw-r--r--core/res/res/values-as/strings.xml33
-rw-r--r--core/res/res/values-az/strings.xml33
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml33
-rw-r--r--core/res/res/values-be/strings.xml33
-rw-r--r--core/res/res/values-bg/strings.xml33
-rw-r--r--core/res/res/values-bn/strings.xml33
-rw-r--r--core/res/res/values-bs/strings.xml33
-rw-r--r--core/res/res/values-ca/strings.xml33
-rw-r--r--core/res/res/values-cs/strings.xml33
-rw-r--r--core/res/res/values-da/strings.xml33
-rw-r--r--core/res/res/values-de/strings.xml33
-rw-r--r--core/res/res/values-el/strings.xml33
-rw-r--r--core/res/res/values-en-rAU/strings.xml33
-rw-r--r--core/res/res/values-en-rCA/strings.xml33
-rw-r--r--core/res/res/values-en-rGB/strings.xml33
-rw-r--r--core/res/res/values-en-rIN/strings.xml33
-rw-r--r--core/res/res/values-es-rUS/strings.xml37
-rw-r--r--core/res/res/values-es/strings.xml33
-rw-r--r--core/res/res/values-et/strings.xml33
-rw-r--r--core/res/res/values-eu/strings.xml41
-rw-r--r--core/res/res/values-fa/strings.xml33
-rw-r--r--core/res/res/values-fi/strings.xml33
-rw-r--r--core/res/res/values-fr-rCA/strings.xml33
-rw-r--r--core/res/res/values-fr/strings.xml33
-rw-r--r--core/res/res/values-gl/strings.xml33
-rw-r--r--core/res/res/values-gu/strings.xml33
-rw-r--r--core/res/res/values-hi/strings.xml33
-rw-r--r--core/res/res/values-hr/strings.xml33
-rw-r--r--core/res/res/values-hu/strings.xml33
-rw-r--r--core/res/res/values-hy/strings.xml33
-rw-r--r--core/res/res/values-in/strings.xml33
-rw-r--r--core/res/res/values-is/strings.xml33
-rw-r--r--core/res/res/values-it/strings.xml33
-rw-r--r--core/res/res/values-iw/strings.xml33
-rw-r--r--core/res/res/values-ja/strings.xml33
-rw-r--r--core/res/res/values-ka/strings.xml33
-rw-r--r--core/res/res/values-kk/strings.xml33
-rw-r--r--core/res/res/values-km/strings.xml33
-rw-r--r--core/res/res/values-kn/strings.xml33
-rw-r--r--core/res/res/values-ko/strings.xml35
-rw-r--r--core/res/res/values-ky/strings.xml33
-rw-r--r--core/res/res/values-lo/strings.xml33
-rw-r--r--core/res/res/values-lt/strings.xml33
-rw-r--r--core/res/res/values-lv/strings.xml33
-rw-r--r--core/res/res/values-mk/strings.xml35
-rw-r--r--core/res/res/values-ml/strings.xml33
-rw-r--r--core/res/res/values-mn/strings.xml33
-rw-r--r--core/res/res/values-ms/strings.xml33
-rw-r--r--core/res/res/values-my/strings.xml33
-rw-r--r--core/res/res/values-nb/strings.xml33
-rw-r--r--core/res/res/values-ne/strings.xml33
-rw-r--r--core/res/res/values-nl/strings.xml33
-rw-r--r--core/res/res/values-or/strings.xml33
-rw-r--r--core/res/res/values-pl/strings.xml33
-rw-r--r--core/res/res/values-pt-rBR/strings.xml33
-rw-r--r--core/res/res/values-pt-rPT/strings.xml33
-rw-r--r--core/res/res/values-pt/strings.xml33
-rw-r--r--core/res/res/values-ro/strings.xml33
-rw-r--r--core/res/res/values-ru/strings.xml33
-rw-r--r--core/res/res/values-sk/strings.xml33
-rw-r--r--core/res/res/values-sl/strings.xml33
-rw-r--r--core/res/res/values-sq/strings.xml33
-rw-r--r--core/res/res/values-sr/strings.xml33
-rw-r--r--core/res/res/values-sv/strings.xml33
-rw-r--r--core/res/res/values-sw/strings.xml33
-rw-r--r--core/res/res/values-ta/strings.xml33
-rw-r--r--core/res/res/values-te/strings.xml33
-rw-r--r--core/res/res/values-th/strings.xml33
-rw-r--r--core/res/res/values-tl/strings.xml33
-rw-r--r--core/res/res/values-tr/strings.xml33
-rw-r--r--core/res/res/values-uk/strings.xml33
-rw-r--r--core/res/res/values-ur/strings.xml33
-rw-r--r--core/res/res/values-uz/strings.xml2
-rw-r--r--core/res/res/values-vi/strings.xml33
-rw-r--r--core/res/res/values-zh-rCN/strings.xml33
-rw-r--r--core/res/res/values-zh-rHK/strings.xml33
-rw-r--r--core/res/res/values-zh-rTW/strings.xml33
-rw-r--r--core/res/res/values-zu/strings.xml33
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/attrs_manifest.xml11
-rw-r--r--core/res/res/values/config.xml119
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/ids.xml9
-rw-r--r--core/res/res/values/policy_exempt_apps.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml)14
-rw-r--r--core/res/res/values/public.xml6
-rw-r--r--core/res/res/values/strings.xml41
-rw-r--r--core/res/res/values/symbols.xml114
-rw-r--r--core/res/res/values/vendor_policy_exempt_apps.xml24
-rw-r--r--core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java96
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java3
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java4
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java4
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java2
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java2
-rw-r--r--core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java112
-rw-r--r--core/tests/coretests/src/android/app/time/TimeConfigurationTest.java56
-rw-r--r--core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java4
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java3
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsTest.java16
-rw-r--r--core/tests/coretests/src/android/content/ContentProviderTest.java8
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerTests.java9
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java181
-rw-r--r--core/tests/coretests/src/android/view/OWNERS3
-rw-r--r--core/tests/coretests/src/android/view/RoundedCornerTest.java7
-rw-r--r--core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java28
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java4
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java4
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java14
-rw-r--r--core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java69
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java14
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java17
-rw-r--r--core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java87
-rw-r--r--core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java13
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java3
-rw-r--r--core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java99
-rw-r--r--core/tests/coretests/src/com/android/internal/view/OWNERS3
-rw-r--r--core/tests/mockingcoretests/src/android/view/DisplayTest.java595
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java296
-rw-r--r--core/tests/uwbtests/src/android/uwb/RangingManagerTest.java110
-rw-r--r--core/tests/uwbtests/src/android/uwb/UwbTestUtils.java29
-rw-r--r--data/etc/car/Android.bp24
-rw-r--r--data/etc/car/com.android.car.cluster.home.xml21
-rw-r--r--data/etc/car/com.android.car.messenger.xml21
-rw-r--r--data/etc/car/com.android.car.shell.xml6
-rw-r--r--data/etc/car/com.android.carsystemui.xml25
-rw-r--r--data/etc/platform.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-rw-r--r--data/etc/services.core.protolog.json66
-rw-r--r--data/keyboards/Vendor_0957_Product_0001.idc1
-rw-r--r--data/keyboards/Vendor_248a_Product_8266.idc24
-rw-r--r--graphics/java/android/graphics/Typeface.java36
-rw-r--r--graphics/java/android/graphics/drawable/RippleAnimationSession.java98
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java77
-rw-r--r--graphics/java/android/graphics/drawable/RippleShader.java126
-rw-r--r--graphics/java/android/graphics/fonts/FontFileUtil.java17
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java26
-rw-r--r--keystore/java/android/security/KeyChain.java21
-rw-r--r--keystore/java/android/security/KeyStore.java14
-rw-r--r--keystore/java/android/security/keystore/AttestationUtils.java55
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java10
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml1
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml6
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java328
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java233
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt40
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt117
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt38
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt26
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt42
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java78
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java80
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java3
-rw-r--r--libs/androidfw/Android.bp2
-rw-r--r--libs/androidfw/AssetManager2.cpp3
-rw-r--r--libs/hwui/Android.bp3
-rw-r--r--libs/hwui/Properties.cpp2
-rw-r--r--libs/hwui/Properties.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp3
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp20
-rw-r--r--libs/hwui/renderthread/VulkanManager.h16
-rw-r--r--libs/hwui/utils/PaintUtils.h12
-rw-r--r--location/java/android/location/CorrelationVector.java3
-rw-r--r--location/java/android/location/GnssNavigationMessage.java8
-rw-r--r--media/Android.bp3
-rw-r--r--media/aidl/android/media/permission/Identity.aidl8
-rw-r--r--media/java/android/media/AudioManager.java81
-rw-r--r--media/java/android/media/AudioRecord.java92
-rw-r--r--media/java/android/media/AudioSystem.java6
-rw-r--r--media/java/android/media/ImageWriter.java3
-rw-r--r--media/java/android/media/MediaDrm.java3
-rw-r--r--media/java/android/media/MediaFormat.java4
-rw-r--r--media/java/android/media/MediaPlayer.java12
-rw-r--r--media/java/android/media/MediaRecorder.java40
-rw-r--r--media/java/android/media/MediaRouter2.java492
-rw-r--r--media/java/android/media/MediaRouter2Manager.java56
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java9
-rw-r--r--media/java/android/media/audiofx/Visualizer.java10
-rw-r--r--media/java/android/media/metrics/NetworkEvent.java5
-rw-r--r--media/java/android/media/metrics/PlaybackErrorEvent.java4
-rw-r--r--media/java/android/media/metrics/PlaybackMetrics.java4
-rw-r--r--media/java/android/media/metrics/PlaybackStateEvent.java4
-rw-r--r--media/java/android/media/metrics/TrackChangeEvent.java2
-rw-r--r--media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl10
-rw-r--r--media/java/android/media/musicrecognition/IMusicRecognitionService.aidl3
-rw-r--r--media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl2
-rw-r--r--media/java/android/media/musicrecognition/MusicRecognitionService.java11
-rw-r--r--media/java/android/media/permission/PermissionUtil.java22
-rw-r--r--media/jni/Android.bp4
-rw-r--r--media/jni/android_media_MediaCodec.cpp2
-rw-r--r--media/jni/android_media_MediaPlayer.cpp11
-rw-r--r--media/jni/android_media_MediaRecorder.cpp10
-rw-r--r--media/jni/audioeffect/Android.bp5
-rw-r--r--media/jni/audioeffect/Visualizer.cpp4
-rw-r--r--media/jni/audioeffect/Visualizer.h5
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp11
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp9
-rw-r--r--media/jni/soundpool/Android.bp5
-rw-r--r--media/jni/soundpool/Sound.cpp12
-rw-r--r--media/jni/soundpool/Stream.cpp15
-rw-r--r--media/jni/soundpool/StreamManager.cpp26
-rw-r--r--media/jni/soundpool/StreamManager.h10
-rw-r--r--media/jni/soundpool/android_media_SoundPool.cpp3
-rw-r--r--media/jni/tuner/DvrClient.cpp5
-rw-r--r--media/jni/tuner/FilterClient.cpp5
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java4
-rw-r--r--packages/CompanionDeviceManager/res/values-af/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-am/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ar/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-as/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-az/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bg/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bs/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-cs/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-da/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-de/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-el/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml9
-rw-r--r--packages/CompanionDeviceManager/res/values-es-rUS/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-es/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-et/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fa/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-gu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hy/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-it/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-iw/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ka/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-kk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-km/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-kn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ko/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lo/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lt/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lv/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ml/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ms/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-my/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ne/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-or/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pa/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-pl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ru/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-si/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sq/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sv/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ta/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-tr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-uk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-uz/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-vi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml12
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java6
-rw-r--r--packages/Connectivity/framework/Android.bp55
-rw-r--r--packages/Connectivity/framework/api/current.txt9
-rw-r--r--packages/Connectivity/framework/api/module-lib-current.txt13
-rw-r--r--packages/Connectivity/framework/api/system-current.txt9
-rw-r--r--packages/Connectivity/framework/jarjar-rules-proto.txt3
-rw-r--r--packages/Connectivity/framework/jarjar-rules.txt10
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityManager.java324
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityResources.java108
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java46
-rw-r--r--packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl15
-rw-r--r--packages/Connectivity/framework/src/android/net/IpPrefix.java2
-rw-r--r--packages/Connectivity/framework/src/android/net/LinkAddress.java2
-rw-r--r--packages/Connectivity/framework/src/android/net/Network.java6
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkAgent.java48
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkCapabilities.java109
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkInfo.java3
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkRequest.java29
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkState.java4
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkUtils.java47
-rw-r--r--packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java8
-rw-r--r--packages/Connectivity/framework/src/android/net/ParseException.java (renamed from core/java/android/net/ParseException.java)9
-rw-r--r--packages/Connectivity/framework/src/android/net/ProxyInfo.java2
-rw-r--r--packages/Connectivity/framework/src/android/net/QosSocketInfo.java6
-rw-r--r--packages/Connectivity/framework/src/android/net/VpnTransportInfo.java13
-rw-r--r--packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java48
-rw-r--r--packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java10
-rw-r--r--packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java26
-rw-r--r--packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl5
-rw-r--r--packages/Connectivity/service/Android.bp4
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/Android.bp35
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml37
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml27
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml27
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml27
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml92
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml32
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml22
-rw-r--r--packages/Connectivity/service/jarjar-rules.txt1
-rw-r--r--packages/CtsShim/apk/arm/CtsShim.apkbin5430 -> 5484 bytes
-rw-r--r--packages/CtsShim/apk/arm/CtsShimPriv.apkbin32077 -> 31680 bytes
-rw-r--r--packages/CtsShim/apk/x86/CtsShim.apkbin5430 -> 5484 bytes
-rw-r--r--packages/CtsShim/apk/x86/CtsShimPriv.apkbin24424 -> 24275 bytes
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java52
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java38
-rw-r--r--packages/SettingsLib/Android.bp10
-rw-r--r--packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java1
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp4
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml (renamed from packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down_24dp.xml)8
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml16
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml33
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml25
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml4
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/values/dimens.xml (renamed from packages/SystemUI/res/drawable/controls_dialog_bg.xml)13
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/values/styles.xml10
-rw-r--r--packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java9
-rw-r--r--packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java3
-rw-r--r--packages/SettingsLib/SettingsTheme/Android.bp4
-rw-r--r--packages/SettingsLib/TwoTargetPreference/Android.bp13
-rw-r--r--packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml (renamed from packages/SettingsLib/res/layout/preference_two_target.xml)2
-rw-r--r--packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml (renamed from packages/SettingsLib/res/layout/preference_two_target_divider.xml)2
-rw-r--r--packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml21
-rw-r--r--packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java (renamed from packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java)9
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values/dimens.xml3
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java (renamed from packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java)12
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java7
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java5
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml7
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/AndroidManifest.xml15
-rw-r--r--packages/SystemUI/plugin/Android.bp5
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java44
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java18
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml59
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml59
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml60
-rw-r--r--packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/color/remote_input_send.xml4
-rw-r--r--packages/SystemUI/res/color/remote_input_text.xml4
-rw-r--r--packages/SystemUI/res/drawable/fingerprint_bg.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_message.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_photo_camera.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_wallet.xml11
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml37
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml35
-rw-r--r--packages/SystemUI/res/drawable/qs_tile_background.xml23
-rw-r--r--packages/SystemUI/res/drawable/qs_tile_background_shape.xml21
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_contents.xml18
-rw-r--r--packages/SystemUI/res/layout/controls_detail_dialog.xml7
-rw-r--r--packages/SystemUI/res/layout/controls_fullscreen.xml (renamed from packages/SystemUI/res/layout/controls_in_dialog.xml)5
-rw-r--r--packages/SystemUI/res/layout/controls_with_favorites.xml15
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml6
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf_row.xml4
-rw-r--r--packages/SystemUI/res/layout/people_space_activity.xml17
-rw-r--r--packages/SystemUI/res/layout/people_space_large_avatar_tile.xml37
-rw-r--r--packages/SystemUI/res/layout/people_space_notification_content_tile.xml154
-rw-r--r--packages/SystemUI/res/layout/people_space_placeholder_layout.xml75
-rw-r--r--packages/SystemUI/res/layout/people_space_small_avatar_tile.xml190
-rw-r--r--packages/SystemUI/res/layout/people_space_small_view.xml59
-rw-r--r--packages/SystemUI/res/layout/qs_carrier.xml14
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml159
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml5
-rw-r--r--packages/SystemUI/res/layout/quick_settings_footer_dialog.xml1
-rw-r--r--packages/SystemUI/res/layout/remote_input.xml7
-rw-r--r--packages/SystemUI/res/layout/udfps_bp_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_bp.xml)4
-rw-r--r--packages/SystemUI/res/layout/udfps_enroll_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_enroll.xml)11
-rw-r--r--packages/SystemUI/res/layout/udfps_fpm_other_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml)10
-rw-r--r--packages/SystemUI/res/layout/udfps_keyguard_view.xml36
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml5
-rw-r--r--packages/SystemUI/res/values/colors.xml6
-rw-r--r--packages/SystemUI/res/values/config.xml11
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/res/values/flags.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml104
-rw-r--r--packages/SystemUI/res/values/styles.xml48
-rw-r--r--packages/SystemUI/res/xml/people_space_widget_info.xml14
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java14
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java14
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java39
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java72
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java28
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java129
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java32
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java38
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java30
-rw-r--r--packages/SystemUI/src/com/android/keyguard/PasswordTextView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java278
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java141
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java190
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java96
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java)19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java)51
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java)78
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java)29
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java)27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java)65
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java202
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java157
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java477
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java215
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java135
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java170
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java375
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/OWNERS12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java401
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java204
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java238
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java219
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java198
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java129
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt149
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java207
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java)37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java11
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java4
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java2
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--rs/java/android/renderscript/Allocation.java6
-rw-r--r--rs/java/android/renderscript/AllocationAdapter.java4
-rw-r--r--rs/java/android/renderscript/BaseObj.java4
-rw-r--r--rs/java/android/renderscript/Byte2.java4
-rw-r--r--rs/java/android/renderscript/Byte3.java4
-rw-r--r--rs/java/android/renderscript/Byte4.java4
-rw-r--r--rs/java/android/renderscript/Double2.java5
-rw-r--r--rs/java/android/renderscript/Double3.java5
-rw-r--r--rs/java/android/renderscript/Double4.java5
-rw-r--r--rs/java/android/renderscript/Element.java5
-rw-r--r--rs/java/android/renderscript/FieldPacker.java4
-rw-r--r--rs/java/android/renderscript/FileA3D.java1
-rw-r--r--rs/java/android/renderscript/Float2.java5
-rw-r--r--rs/java/android/renderscript/Float3.java5
-rw-r--r--rs/java/android/renderscript/Float4.java5
-rw-r--r--rs/java/android/renderscript/Font.java1
-rw-r--r--rs/java/android/renderscript/Int2.java5
-rw-r--r--rs/java/android/renderscript/Int3.java5
-rw-r--r--rs/java/android/renderscript/Int4.java5
-rw-r--r--rs/java/android/renderscript/Long2.java5
-rw-r--r--rs/java/android/renderscript/Long3.java5
-rw-r--r--rs/java/android/renderscript/Long4.java5
-rw-r--r--rs/java/android/renderscript/Matrix2f.java4
-rw-r--r--rs/java/android/renderscript/Matrix3f.java4
-rw-r--r--rs/java/android/renderscript/Matrix4f.java4
-rw-r--r--rs/java/android/renderscript/Mesh.java1
-rw-r--r--rs/java/android/renderscript/Program.java4
-rw-r--r--rs/java/android/renderscript/ProgramFragment.java1
-rw-r--r--rs/java/android/renderscript/ProgramFragmentFixedFunction.java1
-rw-r--r--rs/java/android/renderscript/ProgramRaster.java1
-rw-r--r--rs/java/android/renderscript/ProgramStore.java4
-rw-r--r--rs/java/android/renderscript/ProgramVertex.java2
-rw-r--r--rs/java/android/renderscript/ProgramVertexFixedFunction.java1
-rw-r--r--rs/java/android/renderscript/RSDriverException.java5
-rw-r--r--rs/java/android/renderscript/RSIllegalArgumentException.java5
-rw-r--r--rs/java/android/renderscript/RSInvalidStateException.java5
-rw-r--r--rs/java/android/renderscript/RSRuntimeException.java5
-rw-r--r--rs/java/android/renderscript/RSSurfaceView.java1
-rw-r--r--rs/java/android/renderscript/RSTextureView.java1
-rw-r--r--rs/java/android/renderscript/RenderScript.java5
-rw-r--r--rs/java/android/renderscript/RenderScriptCacheDir.java4
-rw-r--r--rs/java/android/renderscript/RenderScriptGL.java1
-rw-r--r--rs/java/android/renderscript/Sampler.java5
-rw-r--r--rs/java/android/renderscript/Script.java5
-rw-r--r--rs/java/android/renderscript/ScriptC.java5
-rw-r--r--rs/java/android/renderscript/ScriptGroup.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsic.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsic3DLUT.java4
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicBLAS.java4
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicBlend.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicBlur.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicColorMatrix.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java4
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java4
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicHistogram.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicLUT.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicResize.java5
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicYuvToRGB.java5
-rw-r--r--rs/java/android/renderscript/Short2.java5
-rw-r--r--rs/java/android/renderscript/Short3.java5
-rw-r--r--rs/java/android/renderscript/Short4.java5
-rw-r--r--rs/java/android/renderscript/Type.java5
-rw-r--r--rs/jni/Android.mk2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java125
-rw-r--r--services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java370
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java17
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java49
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java38
-rw-r--r--services/api/Android.bp29
-rw-r--r--services/api/current.txt8
-rw-r--r--services/api/non-updatable-current.txt8
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java79
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java25
-rw-r--r--services/core/Android.bp16
-rw-r--r--services/core/java/com/android/server/BatteryService.java132
-rw-r--r--services/core/java/com/android/server/BluetoothDeviceConfigListener.java16
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java46
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java907
-rw-r--r--services/core/java/com/android/server/ConnectivityServiceInitializer.java5
-rw-r--r--services/core/java/com/android/server/ContextHubSystemService.java7
-rw-r--r--services/core/java/com/android/server/IpSecService.java19
-rw-r--r--services/core/java/com/android/server/NetIdManager.java3
-rw-r--r--services/core/java/com/android/server/OWNERS1
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java55
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java92
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java11
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING4
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java90
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java7
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java6
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java118
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerLocal.java39
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java67
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java57
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java50
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java74
-rw-r--r--services/core/java/com/android/server/am/MeasuredEnergySnapshot.java25
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java17
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java36
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java20
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java44
-rw-r--r--services/core/java/com/android/server/am/UserController.java54
-rw-r--r--services/core/java/com/android/server/app/GameManagerSettings.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java48
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java347
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java47
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java45
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java51
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java54
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java5
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java183
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java13
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java8
-rw-r--r--services/core/java/com/android/server/compat/OverrideValidatorImpl.java4
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java11
-rw-r--r--services/core/java/com/android/server/connectivity/DnsManager.java21
-rw-r--r--services/core/java/com/android/server/connectivity/MockableSystemProperties.java7
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java21
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java23
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkDiagnostics.java6
-rw-r--r--services/core/java/com/android/server/connectivity/PacProxyService.java (renamed from services/core/java/com/android/server/connectivity/PacProxyInstaller.java)133
-rw-r--r--services/core/java/com/android/server/connectivity/PermissionMonitor.java66
-rw-r--r--services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java87
-rw-r--r--services/core/java/com/android/server/connectivity/ProxyTracker.java45
-rw-r--r--services/core/java/com/android/server/content/ContentService.java3
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java16
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java4
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java26
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java31
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java29
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java81
-rw-r--r--services/core/java/com/android/server/display/HighBrightnessModeController.java26
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java13
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java2
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java7
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java23
-rw-r--r--services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java56
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectAction.java27
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java433
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecNetwork.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java8
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java63
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java17
-rw-r--r--services/core/java/com/android/server/hdmi/cec_config.xml133
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java22
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java37
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java4
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java19
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java74
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java2
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java2
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssStatusProvider.java2
-rw-r--r--services/core/java/com/android/server/location/gnss/hal/GnssNative.java194
-rw-r--r--services/core/java/com/android/server/location/injector/LocationUsageLogger.java12
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java2
-rw-r--r--services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java107
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java10
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java57
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java8
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java8
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java5
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java12
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java33
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java249
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java60
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java2
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java47
-rw-r--r--services/core/java/com/android/server/pm/ComponentResolver.java2
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java26
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java27
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java130
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java434
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java262
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java4
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java3
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java32
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java20
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java393
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java8
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java69
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java35
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java10
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java20
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java165
-rw-r--r--services/core/java/com/android/server/policy/SplashScreenSurface.java2
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java12
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java6
-rw-r--r--services/core/java/com/android/server/power/FaceDownDetector.java77
-rw-r--r--services/core/java/com/android/server/power/Notifier.java7
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java7
-rw-r--r--services/core/java/com/android/server/power/PreRebootLogger.java4
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java43
-rw-r--r--services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java33
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java11
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java6
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java7
-rw-r--r--services/core/java/com/android/server/security/FileIntegrityService.java1
-rw-r--r--services/core/java/com/android/server/security/OWNERS1
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java31
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java16
-rw-r--r--services/core/java/com/android/server/stats/OWNERS3
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java21
-rw-r--r--services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java2
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java9
-rw-r--r--services/core/java/com/android/server/timedetector/ConfigurationInternal.java123
-rw-r--r--services/core/java/com/android/server/timedetector/EnvironmentImpl.java19
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java45
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java4
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java13
-rw-r--r--services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java45
-rw-r--r--services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java4
-rw-r--r--services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java344
-rw-r--r--services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java49
-rw-r--r--services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java9
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java4
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java11
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java51
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java6
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java15
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java66
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java42
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java30
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java12
-rw-r--r--services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java53
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java4
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java25
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java1153
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java121
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java55
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java211
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java7
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java2
-rw-r--r--services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java9
-rw-r--r--services/core/java/com/android/server/wm/BlurController.java30
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java22
-rw-r--r--services/core/java/com/android/server/wm/DisplayHashController.java116
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java20
-rw-r--r--services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java35
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java2
-rw-r--r--services/core/java/com/android/server/wm/DragState.java44
-rw-r--r--services/core/java/com/android/server/wm/FixedRotationAnimationController.java11
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java28
-rw-r--r--services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java101
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java146
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java42
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java57
-rw-r--r--services/core/java/com/android/server/wm/SeamlessRotator.java20
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java51
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java43
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java34
-rw-r--r--services/core/java/com/android/server/wm/TaskPersister.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java7
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java101
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java184
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java57
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp13
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp11
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp69
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/core/xsd/Android.bp7
-rw-r--r--services/core/xsd/cec-config/cec-config.xsd29
-rw-r--r--services/core/xsd/cec-config/schema/current.txt44
-rw-r--r--services/core/xsd/cec-config/schema/last_current.txt0
-rw-r--r--services/core/xsd/cec-config/schema/last_removed.txt0
-rw-r--r--services/core/xsd/cec-config/schema/removed.txt1
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java47
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java24
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java31
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java34
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java371
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java41
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java12
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java25
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java5
-rw-r--r--services/incremental/Android.bp2
-rw-r--r--services/incremental/IncrementalService.cpp216
-rw-r--r--services/incremental/IncrementalService.h18
-rw-r--r--services/incremental/ServiceWrappers.cpp16
-rw-r--r--services/incremental/ServiceWrappers.h10
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp317
-rw-r--r--services/java/com/android/server/SystemServer.java18
-rw-r--r--services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java65
-rw-r--r--services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java24
-rw-r--r--services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java9
-rw-r--r--services/tests/PackageManagerServiceTests/unit/Android.bp7
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt5
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt278
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java48
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt372
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt425
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt12
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt18
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt31
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt15
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java254
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java187
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java (renamed from services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java)181
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java (renamed from services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java)148
-rw-r--r--services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS1
-rw-r--r--services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS1
-rw-r--r--services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS1
-rw-r--r--services/tests/servicestests/assets/OwnersTest/OWNERS1
-rw-r--r--services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java203
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java241
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java200
-rw-r--r--services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java248
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java150
-rw-r--r--services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java66
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java99
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java353
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java856
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java464
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java343
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java117
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java300
-rw-r--r--services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java56
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java57
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java131
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java141
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml4
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS1
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java113
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java12
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java128
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java459
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java46
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestIWindow.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java2
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerService.java23
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java53
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProto.java7
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProtoV2.java6
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java19
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java33
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java43
-rw-r--r--telecomm/java/android/telecom/CallDiagnosticService.java218
-rw-r--r--telecomm/java/android/telecom/CallDiagnostics.java374
-rw-r--r--telecomm/java/android/telecom/Conference.java2
-rw-r--r--telecomm/java/android/telecom/Connection.java6
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java45
-rw-r--r--telecomm/java/android/telecom/DiagnosticCall.java358
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java89
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java4
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl2
-rw-r--r--telephony/java/android/telephony/CarrierBandwidth.java8
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java446
-rw-r--r--telephony/java/android/telephony/LinkCapacityEstimate.aidl19
-rw-r--r--telephony/java/android/telephony/LinkCapacityEstimate.java179
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java69
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java137
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java12
-rw-r--r--telephony/java/android/telephony/data/DataService.java34
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java8
-rw-r--r--telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java10
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java8
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java4
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java26
-rw-r--r--telephony/java/android/telephony/ims/RcsContactPresenceTuple.java25
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java90
-rw-r--r--telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl1
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java25
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java35
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl43
-rw-r--r--tests/FlickerTests/AndroidTest.xml2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt128
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt128
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt141
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt70
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt54
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt51
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt49
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt133
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt120
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt154
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt127
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt37
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt21
-rw-r--r--tests/Input/Android.bp15
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java98
-rw-r--r--tests/Input/src/com/android/test/input/InputEventAssignerTest.kt2
-rw-r--r--tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt123
-rw-r--r--tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java13
-rw-r--r--tests/RollbackTest/MultiUserRollbackTest.xml2
-rw-r--r--tests/RollbackTest/NetworkStagedRollbackTest.xml2
-rw-r--r--tests/RollbackTest/RollbackTest.xml2
-rw-r--r--tests/RollbackTest/StagedRollbackTest.xml2
-rw-r--r--tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp25
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt77
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt8
-rw-r--r--tests/net/Android.bp1
-rw-r--r--tests/net/TEST_MAPPING17
-rw-r--r--tests/net/common/java/android/net/IpPrefixTest.java9
-rw-r--r--tests/net/common/java/android/net/LinkAddressTest.java15
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java112
-rw-r--r--tests/net/common/java/android/net/NetworkProviderTest.kt7
-rw-r--r--tests/net/integration/util/com/android/server/NetworkAgentWrapper.java29
-rw-r--r--tests/net/java/android/net/ConnectivityManagerTest.java26
-rw-r--r--tests/net/java/android/net/util/KeepaliveUtilsTest.kt26
-rw-r--r--tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt13
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java990
-rw-r--r--tests/net/java/com/android/server/IpSecServiceTest.java10
-rw-r--r--tests/net/java/com/android/server/LegacyTypeTrackerTest.kt127
-rw-r--r--tests/net/java/com/android/server/connectivity/DnsManagerTest.java6
-rw-r--r--tests/net/java/com/android/server/connectivity/LingerMonitorTest.java8
-rw-r--r--tests/net/java/com/android/server/connectivity/Nat464XlatTest.java79
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java48
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java8
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java9
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java12
-rw-r--r--tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java26
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java4
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java10
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java3
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java2
1693 files changed, 49634 insertions, 21997 deletions
diff --git a/.prebuilt_info/OWNERS b/.prebuilt_info/OWNERS
new file mode 100644
index 000000000000..eb8b89b10e83
--- /dev/null
+++ b/.prebuilt_info/OWNERS
@@ -0,0 +1 @@
+per-file prebuilt_info_packages_CtsShim_*.asciipb = file:/packages/CtsShim/OWNERS
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 29bcfe046b98..1bd90a8eafae 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7197701"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
@@ -8,5 +8,5 @@ drops {
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index be172e6122cf..544bca029888 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7197701"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
@@ -8,5 +8,5 @@ drops {
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 13eca13c9308..72386bb88465 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7197701"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
@@ -8,5 +8,5 @@ drops {
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 2e863fe57f3c..893eac214daa 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "6508977"
+ build_id: "7197701"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
@@ -8,5 +8,5 @@ drops {
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "rvc-dev"
+ git_branch: "sc-dev"
}
diff --git a/Android.bp b/Android.bp
index c5980b911b17..ee5e992935ae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -584,7 +584,7 @@ java_library {
"android.hardware.vibrator-V2-java",
"android.security.apc-java",
"android.security.authorization-java",
- "android.security.usermanager-java",
+ "android.security.maintenance-java",
"android.security.vpnprofilestore-java",
"android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
@@ -665,9 +665,8 @@ java_defaults {
],
required: [
"framework-platform-compat-config",
- // TODO: remove gps_debug, cec_config.xml and protolog.conf.json when the build system propagates "required" properly.
+ // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
"gps_debug.conf",
- "cec_config.xml",
"icu4j-platform-compat-config",
"libcore-platform-compat-config",
"protolog.conf.json.gz",
@@ -918,7 +917,6 @@ filegroup {
"core/java/com/android/internal/util/RingBufferIndices.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/com/android/internal/util/TokenBucket.java",
],
@@ -945,7 +943,6 @@ filegroup {
"core/java/com/android/internal/util/MessageUtils.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
],
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bff222e7d42f..c61f7c684efd 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -437,10 +437,6 @@ droidstubs {
name: "hwbinder-stubs-docs",
srcs: [
"core/java/android/os/HidlSupport.java",
- "core/java/android/annotation/IntDef.java",
- "core/java/android/annotation/IntRange.java",
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/SystemApi.java",
"core/java/android/os/HidlMemory.java",
"core/java/android/os/HwBinder.java",
"core/java/android/os/HwBlob.java",
@@ -466,6 +462,7 @@ droidstubs {
java_library_static {
name: "hwbinder.stubs",
sdk_version: "core_current",
+ libs: ["stub-annotations"],
srcs: [
":hwbinder-stubs-docs",
],
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d08c52782fb3..9ceef6bbe8a3 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -82,5 +82,65 @@
{
"name": "ManagedProfileLifecycleStressTest"
}
- ]
+ ],
+ "auto-postsubmit": [
+ // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+ // automotive targets to avoid introducing additional test flake and build time. The plan for
+ // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+ // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+ // infra during the hardening phase.
+ // TODO: this tag to be removed once the above is no longer an issue.
+ {
+ "name": "FrameworksUiServicesTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "ExtServicesUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TestablesTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 5ded446ff81a..f6bfaa356799 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -18,9 +18,10 @@ package android.app.appsearch {
}
public static final class AppSearchManager.SearchContext.Builder {
- ctor public AppSearchManager.SearchContext.Builder();
+ ctor @Deprecated public AppSearchManager.SearchContext.Builder();
+ ctor public AppSearchManager.SearchContext.Builder(@NonNull String);
method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build();
- method @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
}
public interface AppSearchMigrationHelper {
@@ -115,7 +116,6 @@ package android.app.appsearch {
public abstract static class AppSearchSchema.PropertyConfig {
method public int getCardinality();
- method public int getDataType();
method @NonNull public String getName();
field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
field public static final int CARDINALITY_REPEATED = 1; // 0x1
@@ -180,14 +180,15 @@ package android.app.appsearch {
method public int getScore();
method public long getTtlMillis();
method @NonNull public String getUri();
- field public static final String DEFAULT_NAMESPACE = "";
+ field @Deprecated public static final String DEFAULT_NAMESPACE = "";
}
public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
- ctor public GenericDocument.Builder(@NonNull String, @NonNull String);
+ ctor @Deprecated public GenericDocument.Builder(@NonNull String, @NonNull String);
+ ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String);
method @NonNull public android.app.appsearch.GenericDocument build();
method @NonNull public BuilderType setCreationTimestampMillis(long);
- method @NonNull public BuilderType setNamespace(@NonNull String);
+ method @Deprecated @NonNull public BuilderType setNamespace(@NonNull String);
method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
@@ -206,12 +207,13 @@ package android.app.appsearch {
}
public static final class GetByUriRequest.Builder {
- ctor public GetByUriRequest.Builder();
+ ctor @Deprecated public GetByUriRequest.Builder();
+ ctor public GetByUriRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest build();
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
}
public class GlobalSearchSession implements java.io.Closeable {
@@ -242,11 +244,12 @@ package android.app.appsearch {
}
public static final class RemoveByUriRequest.Builder {
- ctor public RemoveByUriRequest.Builder();
+ ctor @Deprecated public RemoveByUriRequest.Builder();
+ ctor public RemoveByUriRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.RemoveByUriRequest build();
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
}
public final class ReportUsageRequest {
@@ -256,30 +259,50 @@ package android.app.appsearch {
}
public static final class ReportUsageRequest.Builder {
- ctor public ReportUsageRequest.Builder();
+ ctor @Deprecated public ReportUsageRequest.Builder();
+ ctor public ReportUsageRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest build();
- method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
+ method @Deprecated @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long);
}
public final class SearchResult {
method @NonNull public String getDatabaseName();
- method @NonNull public android.app.appsearch.GenericDocument getDocument();
+ method @Deprecated @NonNull public android.app.appsearch.GenericDocument getDocument();
+ method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
method @NonNull public String getPackageName();
}
+ public static final class SearchResult.Builder {
+ ctor public SearchResult.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo);
+ method @NonNull public android.app.appsearch.SearchResult build();
+ method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument);
+ }
+
public static final class SearchResult.MatchInfo {
method @NonNull public CharSequence getExactMatch();
- method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+ method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+ method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange();
method @NonNull public String getFullText();
method @NonNull public String getPropertyPath();
method @NonNull public CharSequence getSnippet();
- method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+ method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+ method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange();
+ }
+
+ public static final class SearchResult.MatchInfo.Builder {
+ ctor public SearchResult.MatchInfo.Builder();
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo build();
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setPropertyPath(@NonNull String);
+ method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
}
public static final class SearchResult.MatchRange {
+ ctor public SearchResult.MatchRange(int, int);
method public int getEnd();
method public int getStart();
}
@@ -361,6 +384,19 @@ package android.app.appsearch {
method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures();
}
+ public static final class SetSchemaResponse.Builder {
+ ctor public SetSchemaResponse.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailure(@NonNull android.app.appsearch.SetSchemaResponse.MigrationFailure);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailures(@NonNull java.util.Collection<android.app.appsearch.SetSchemaResponse.MigrationFailure>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse build();
+ }
+
public static class SetSchemaResponse.MigrationFailure {
method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult();
method @NonNull public String getNamespace();
@@ -368,11 +404,23 @@ package android.app.appsearch {
method @NonNull public String getUri();
}
+ public static final class SetSchemaResponse.MigrationFailure.Builder {
+ ctor public SetSchemaResponse.MigrationFailure.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure build();
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setAppSearchResult(@NonNull android.app.appsearch.AppSearchResult<java.lang.Void>);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setNamespace(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setSchemaType(@NonNull String);
+ method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setUri(@NonNull String);
+ }
+
}
package android.app.appsearch.exceptions {
public class AppSearchException extends java.lang.Exception {
+ ctor public AppSearchException(int);
+ ctor public AppSearchException(int, @Nullable String);
+ ctor public AppSearchException(int, @Nullable String, @Nullable Throwable);
method public int getResultCode();
method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult();
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index da446bf1e7c6..0c6b86b68636 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -19,19 +19,10 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.Bundle;
-import android.os.ParcelableException;
-import android.os.RemoteException;
-import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -39,17 +30,19 @@ import java.util.function.Consumer;
* Provides access to the centralized AppSearch index maintained by the system.
*
* <p>AppSearch is a search library for managing structured data featuring:
+ *
* <ul>
- * <li>A fully offline on-device solution
- * <li>A set of APIs for applications to index documents and retrieve them via full-text search
- * <li>APIs for applications to allow the System to display their content on system UI surfaces
- * <li>Similarly, APIs for applications to allow the System to share their content with other
- * specified applications.
+ * <li>A fully offline on-device solution
+ * <li>A set of APIs for applications to index documents and retrieve them via full-text search
+ * <li>APIs for applications to allow the System to display their content on system UI surfaces
+ * <li>Similarly, APIs for applications to allow the System to share their content with other
+ * specified applications.
* </ul>
*
* <p>Applications create a database by opening an {@link AppSearchSession}.
*
* <p>Example:
+ *
* <pre>
* AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
*
@@ -60,11 +53,12 @@ import java.util.function.Consumer;
* });</pre>
*
* <p>After opening the session, a schema must be set in order to define the organizational
- * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema
- * is composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique
- * type of data.
+ * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema is
+ * composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique type
+ * of data.
*
* <p>Example:
+ *
* <pre>
* AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
* .addProperty(new StringPropertyConfig.Builder("subject")
@@ -82,15 +76,16 @@ import java.util.function.Consumer;
* });</pre>
*
* <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object,
- * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a
- * logical group of documents. For example, a namespace can be created to group documents on a
- * per-account basis. A URI identifies a single document within a namespace. The combination
- * of URI and namespace uniquely identifies a {@link GenericDocument} in the database.
+ * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a logical
+ * group of documents. For example, a namespace can be created to group documents on a per-account
+ * basis. A URI identifies a single document within a namespace. The combination of URI and
+ * namespace uniquely identifies a {@link GenericDocument} in the database.
*
- * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database
- * and indexed by calling {@link AppSearchSession#put}.
+ * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database and
+ * indexed by calling {@link AppSearchSession#put}.
*
* <p>Example:
+ *
* <pre>
* // Although for this example we use GenericDocument directly, we recommend extending
* // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
@@ -115,18 +110,12 @@ import java.util.function.Consumer;
* and namespace.
*
* <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove
- * operation. Remove operations can be done by URI and namespace via
- * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)},
- * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
+ * operation. Remove operations can be done by URI and namespace via {@link
+ * AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)}, or by query via
+ * {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
*/
@SystemService(Context.APP_SEARCH_SERVICE)
public class AppSearchManager {
- /**
- * The default empty database name.
- *
- * @hide
- */
- public static final String DEFAULT_DATABASE_NAME = "";
private final IAppSearchManager mService;
private final Context mContext;
@@ -158,10 +147,42 @@ public class AppSearchManager {
/** Builder for {@link SearchContext} objects. */
public static final class Builder {
- private String mDatabaseName = DEFAULT_DATABASE_NAME;
+ private String mDatabaseName;
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
+ * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
+ * method exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mDatabaseName = "";
+ }
+
+ /**
+ * Creates a new {@link SearchContext.Builder}.
+ *
+ * <p>{@link AppSearchSession} will create or open a database under the given name.
+ *
+ * <p>Databases with different names are fully separate with distinct types, namespaces,
+ * and data.
+ *
+ * <p>Database name cannot contain {@code '/'}.
+ *
+ * @param databaseName The name of the database.
+ * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+ */
+ public Builder(@NonNull String databaseName) {
+ Objects.requireNonNull(databaseName);
+ Preconditions.checkArgument(
+ !databaseName.contains("/"), "Database name cannot contain '/'");
+ mDatabaseName = databaseName;
+ }
+
+ /**
* Sets the name of the database associated with {@link AppSearchSession}.
*
* <p>{@link AppSearchSession} will create or open a database under the given name.
@@ -173,16 +194,21 @@ public class AppSearchManager {
*
* <p>If not specified, defaults to the empty string.
*
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
* @param databaseName The name of the database.
* @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+ * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
+ * method exists only for dogfooder transition and must be removed.
*/
+ @Deprecated
@NonNull
public Builder setDatabaseName(@NonNull String databaseName) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(databaseName);
- if (databaseName.contains("/")) {
- throw new IllegalArgumentException("Database name cannot contain '/'");
- }
+ Preconditions.checkArgument(
+ !databaseName.contains("/"), "Database name cannot contain '/'");
mDatabaseName = databaseName;
return this;
}
@@ -246,349 +272,9 @@ public class AppSearchManager {
mService, mContext.getUserId(), getPackageName(), executor, callback);
}
- /**
- * Sets the schema being used by documents provided to the {@link #putDocuments} method.
- *
- * <p>The schema provided here is compared to the stored copy of the schema previously supplied
- * to {@link #setSchema}, if any, to determine how to treat existing documents. The following
- * types of schema modifications are always safe and are made without deleting any existing
- * documents:
- *
- * <ul>
- * <li>Addition of new types
- * <li>Addition of new {@link
- * android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
- * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED
- * REPEATED} properties to a type
- * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link
- * android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL}
- * property into a {@link
- * android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED}
- * property.
- * </ul>
- *
- * <p>The following types of schema changes are not backwards-compatible:
- *
- * <ul>
- * <li>Removal of an existing type
- * <li>Removal of a property from a type
- * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
- * <li>For properties of {@code GenericDocument} type, changing the schema type of {@code
- * GenericDocument}s of that property
- * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link
- * android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL}
- * property into a {@link
- * android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED}
- * property).
- * <li>Adding a {@link
- * android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED}
- * property.
- * </ul>
- *
- * <p>Supplying a schema with such changes will result in this call returning an {@link
- * AppSearchResult} with a code of {@link AppSearchResult#RESULT_INVALID_SCHEMA} and an error
- * message describing the incompatibility. In this case the previously set schema will remain
- * active.
- *
- * <p>If you need to make non-backwards-compatible changes as described above, instead use the
- * {@link #setSchema(List, boolean)} method with the {@code forceOverride} parameter set to
- * {@code true}.
- *
- * <p>It is a no-op to set the same schema as has been previously set; this is handled
- * efficiently.
- *
- * @param request The schema update request.
- * @return the result of performing this operation.
- * @hide
- * @deprecated use {@link AppSearchSession#setSchema} instead.
- */
- @NonNull
- public AppSearchResult<Void> setSchema(@NonNull SetSchemaRequest request) {
- Preconditions.checkNotNull(request);
- // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to
- // avoid binder limits.
- List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
- for (AppSearchSchema schema : request.getSchemas()) {
- schemaBundles.add(schema.getBundle());
- }
- AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
- try {
- mService.setSchema(
- getPackageName(),
- DEFAULT_DATABASE_NAME,
- schemaBundles,
- new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
- request.isForceOverride(),
- mContext.getUserId(),
- new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- future.complete(result);
- }
- });
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return getFutureOrThrow(future);
- }
-
- /**
- * Index {@link GenericDocument}s into AppSearch.
- *
- * <p>You should not call this method directly; instead, use the {@code
- * AppSearch#putDocuments()} API provided by JetPack.
- *
- * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
- * schema type previously registered via the {@link #setSchema} method.
- *
- * @param request {@link PutDocumentsRequest} containing documents to be indexed
- * @return The pending result of performing this operation. The keys of the returned {@link
- * AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if
- * they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
- * @throws RuntimeException If an error occurred during the execution.
- * @hide
- * @deprecated use {@link AppSearchSession#put} instead.
- */
- public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
- // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
- // one big list.
- List<GenericDocument> documents = request.getGenericDocuments();
- List<Bundle> documentBundles = new ArrayList<>(documents.size());
- for (int i = 0; i < documents.size(); i++) {
- documentBundles.add(documents.get(i).getBundle());
- }
- AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
- try {
- mService.putDocuments(
- getPackageName(),
- DEFAULT_DATABASE_NAME,
- documentBundles,
- mContext.getUserId(),
- new IAppSearchBatchResultCallback.Stub() {
- public void onResult(AppSearchBatchResult result) {
- future.complete(result);
- }
-
- public void onSystemError(ParcelableException exception) {
- future.completeExceptionally(exception);
- }
- });
- } catch (RemoteException e) {
- future.completeExceptionally(e);
- }
- return getFutureOrThrow(future);
- }
-
- /**
- * Retrieves {@link GenericDocument}s by URI.
- *
- * <p>You should not call this method directly; instead, use the {@code
- * AppSearch#getDocuments()} API provided by JetPack.
- *
- * @param request {@link GetByUriRequest} containing URIs to be retrieved.
- * @return The pending result of performing this operation. The keys of the returned {@link
- * AppSearchBatchResult} are the input URIs. The values are the returned {@link
- * GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise. URIs that
- * are not found will return a failed {@link AppSearchResult} with a result code of {@link
- * AppSearchResult#RESULT_NOT_FOUND}.
- * @throws RuntimeException If an error occurred during the execution.
- * @hide
- * @deprecated use {@link AppSearchSession#getByUri} instead.
- */
- public AppSearchBatchResult<String, GenericDocument> getByUri(
- @NonNull GetByUriRequest request) {
- // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
- // them in one big list.
- List<String> uris = new ArrayList<>(request.getUris());
- AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
- try {
- mService.getDocuments(
- getPackageName(),
- DEFAULT_DATABASE_NAME,
- request.getNamespace(),
- uris,
- request.getProjectionsInternal(),
- mContext.getUserId(),
- new IAppSearchBatchResultCallback.Stub() {
- public void onResult(AppSearchBatchResult result) {
- future.complete(result);
- }
-
- public void onSystemError(ParcelableException exception) {
- future.completeExceptionally(exception);
- }
- });
- } catch (RemoteException e) {
- future.completeExceptionally(e);
- }
-
- // Translate from document bundles to GenericDocument instances
- AppSearchBatchResult<String, Bundle> bundleResult = getFutureOrThrow(future);
- AppSearchBatchResult.Builder<String, GenericDocument> documentResultBuilder =
- new AppSearchBatchResult.Builder<>();
-
- // Translate successful results
- for (Map.Entry<String, Bundle> bundleEntry : bundleResult.getSuccesses().entrySet()) {
- GenericDocument document;
- try {
- document = new GenericDocument(bundleEntry.getValue());
- } catch (Throwable t) {
- // These documents went through validation, so how could this fail? We must have
- // done something wrong.
- documentResultBuilder.setFailure(
- bundleEntry.getKey(),
- AppSearchResult.RESULT_INTERNAL_ERROR,
- t.getMessage());
- continue;
- }
- documentResultBuilder.setSuccess(bundleEntry.getKey(), document);
- }
-
- // Translate failed results
- for (Map.Entry<String, AppSearchResult<Bundle>> bundleEntry :
- bundleResult.getFailures().entrySet()) {
- documentResultBuilder.setFailure(
- bundleEntry.getKey(),
- bundleEntry.getValue().getResultCode(),
- bundleEntry.getValue().getErrorMessage());
- }
-
- return documentResultBuilder.build();
- }
-
- /**
- * Searches a document based on a given query string.
- *
- * <p>You should not call this method directly; instead, use the {@code AppSearch#query()} API
- * provided by JetPack.
- *
- * <p>Currently we support following features in the raw query format:
- *
- * <ul>
- * <li>AND
- * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and ‘cat’”).
- * Example: hello world matches documents that have both ‘hello’ and ‘world’
- * <li>OR
- * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or ‘cat’”). Example:
- * dog OR puppy
- * <li>Exclusion
- * <p>Exclude a term (e.g. “match documents that do not have the term ‘dog’”). Example:
- * -dog excludes the term ‘dog’
- * <li>Grouping terms
- * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
- * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
- * Example: (dog puppy) (cat kitten) two one group containing two terms.
- * <li>Property restricts
- * <p>Specifies which properties of a document to specifically match terms in (e.g. “match
- * documents where the ‘subject’ property contains ‘important’”). Example:
- * subject:important matches documents with the term ‘important’ in the ‘subject’ property
- * <li>Schema type restricts
- * <p>This is similar to property restricts, but allows for restricts on top-level
- * document fields, such as schema_type. Clients should be able to limit their query to
- * documents of a certain schema_type (e.g. “match documents that are of the ‘Email’
- * schema_type”). Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will
- * match documents that contain the query term ‘dog’ and are of either the ‘Email’ schema
- * type or the ‘Video’ schema type.
- * </ul>
- *
- * @param queryExpression Query String to search.
- * @param searchSpec Spec for setting filters, raw query etc.
- * @throws RuntimeException If an error occurred during the execution.
- * @hide
- * @deprecated use AppSearchSession#query instead.
- */
- @NonNull
- public AppSearchResult<List<SearchResult>> query(
- @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
- // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
- // them in one big list.
- AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
- try {
- mService.query(
- getPackageName(),
- DEFAULT_DATABASE_NAME,
- queryExpression,
- searchSpec.getBundle(),
- mContext.getUserId(),
- new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- future.complete(result);
- }
- });
- AppSearchResult<Bundle> bundleResult = getFutureOrThrow(future);
- if (!bundleResult.isSuccess()) {
- return AppSearchResult.newFailedResult(
- bundleResult.getResultCode(), bundleResult.getErrorMessage());
- }
- SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue());
- return AppSearchResult.newSuccessfulResult(searchResultPage.getResults());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (Throwable t) {
- return AppSearchResult.throwableToFailedResult(t);
- }
- }
-
- /**
- * Removes {@link GenericDocument}s by URI.
- *
- * <p>You should not call this method directly; instead, use the {@code AppSearch#delete()} API
- * provided by JetPack.
- *
- * @param request Request containing URIs to be removed.
- * @return The pending result of performing this operation. The keys of the returned {@link
- * AppSearchBatchResult} are the input URIs. The values are {@code null} on success, or a
- * failed {@link AppSearchResult} otherwise. URIs that are not found will return a failed
- * {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
- * @throws RuntimeException If an error occurred during the execution.
- * @hide
- * @deprecated use {@link AppSearchSession#remove} instead.
- */
- public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
- List<String> uris = new ArrayList<>(request.getUris());
- AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
- try {
- mService.removeByUri(
- getPackageName(),
- DEFAULT_DATABASE_NAME,
- request.getNamespace(),
- uris,
- mContext.getUserId(),
- new IAppSearchBatchResultCallback.Stub() {
- public void onResult(AppSearchBatchResult result) {
- future.complete(result);
- }
-
- public void onSystemError(ParcelableException exception) {
- future.completeExceptionally(exception);
- }
- });
- } catch (RemoteException e) {
- future.completeExceptionally(e);
- }
- return getFutureOrThrow(future);
- }
-
/** Returns the package name that should be used for uid verification. */
@NonNull
private String getPackageName() {
return mContext.getOpPackageName();
}
-
- private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
- try {
- return future.get();
- } catch (Throwable e) {
- if (e instanceof ExecutionException) {
- e = e.getCause();
- }
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
- if (e instanceof Error) {
- throw (Error) e;
- }
- throw new RuntimeException(e);
- }
- }
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index f379739a3778..486acb4a3d7e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -241,6 +241,7 @@ public final class AppSearchSession implements Closeable {
documentBundles.add(documents.get(i).getBundle());
}
try {
+ // TODO(b/173532925) a timestamp needs to be sent here to calculate binder latency
mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
index d3949047ec5d..77740f88de7a 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
@@ -152,14 +152,14 @@ public class AppSearchEmail extends GenericDocument {
/** The builder class for {@link AppSearchEmail}. */
public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
-
/**
* Creates a new {@link AppSearchEmail.Builder}
*
+ * @param namespace The namespace of the Email.
* @param uri The Uri of the Email.
*/
- public Builder(@NonNull String uri) {
- super(uri, SCHEMA_TYPE);
+ public Builder(@NonNull String namespace, @NonNull String uri) {
+ super(namespace, uri, SCHEMA_TYPE);
}
/** Sets the from address of {@link AppSearchEmail} */
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 8bf438d43cd1..2cf52716cffc 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -309,7 +309,11 @@ public final class AppSearchSchema {
return mBundle.getString(NAME_FIELD, "");
}
- /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
+ /**
+ * Returns the type of data the property contains (e.g. string, int, bytes, etc).
+ *
+ * @hide
+ */
public @DataType int getDataType() {
return mBundle.getInt(DATA_TYPE_FIELD, -1);
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 72bb9f3d07c8..4ce95ea358f4 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -46,8 +46,14 @@ import java.util.Set;
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
- /** The default empty namespace. */
- public static final String DEFAULT_NAMESPACE = "";
+ /**
+ * The default empty namespace.
+ *
+ * <p>TODO(b/181887768): This exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated This exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated public static final String DEFAULT_NAMESPACE = "";
/** The maximum number of elements in a repeatable field. */
private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
@@ -141,7 +147,7 @@ public class GenericDocument {
/** Returns the namespace of the {@link GenericDocument}. */
@NonNull
public String getNamespace() {
- return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+ return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ "");
}
/** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */
@@ -579,6 +585,9 @@ public class GenericDocument {
*
* <p>Once {@link #build} is called, the instance can no longer be used.
*
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
* @param uri the URI to set for the {@link GenericDocument}.
* @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
* provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
@@ -586,7 +595,10 @@ public class GenericDocument {
* using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
* {@link AppSearchSession#put} with result code {@link
* AppSearchResult#RESULT_NOT_FOUND}.
+ * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
+ * instead. This method exists only for dogfooder transition and must be removed.
*/
+ @Deprecated
@SuppressWarnings("unchecked")
public Builder(@NonNull String uri, @NonNull String schemaType) {
Preconditions.checkNotNull(uri);
@@ -604,6 +616,41 @@ public class GenericDocument {
}
/**
+ * Creates a new {@link GenericDocument.Builder}.
+ *
+ * <p>Once {@link #build} is called, the instance can no longer be used.
+ *
+ * <p>URIs are unique within a namespace.
+ *
+ * <p>The number of namespaces per app should be kept small for efficiency reasons.
+ *
+ * @param namespace the namespace to set for the {@link GenericDocument}.
+ * @param uri the URI to set for the {@link GenericDocument}.
+ * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
+ * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
+ * prior to inserting a document of this {@code schemaType} into the AppSearch index
+ * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
+ * {@link AppSearchSession#put} with result code {@link
+ * AppSearchResult#RESULT_NOT_FOUND}.
+ */
+ @SuppressWarnings("unchecked")
+ public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) {
+ Preconditions.checkNotNull(namespace);
+ Preconditions.checkNotNull(uri);
+ Preconditions.checkNotNull(schemaType);
+ mBuilderTypeInstance = (BuilderType) this;
+ mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
+ mBundle.putString(GenericDocument.URI_FIELD, uri);
+ mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
+ // Set current timestamp for creation timestamp by default.
+ mBundle.putLong(
+ GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
+ mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+ mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
+ mBundle.putBundle(PROPERTIES_FIELD, mProperties);
+ }
+
+ /**
* Sets the app-defined namespace this document resides in. No special values are reserved
* or understood by the infrastructure.
*
@@ -611,8 +658,14 @@ public class GenericDocument {
*
* <p>The number of namespaces per app should be kept small for efficiency reasons.
*
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
+ *
* @throws IllegalStateException if the builder has already been used.
+ * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
+ * instead. This method exists only for dogfooder transition and must be removed.
*/
+ @Deprecated
@NonNull
public BuilderType setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 17266f82b603..6881a27d6846 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -107,19 +107,40 @@ public final class GetByUriRequest {
* <p>Once {@link #build} is called, the instance can no longer be used.
*/
public static final class Builder {
- private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ private String mNamespace;
private final Set<String> mUris = new ArraySet<>();
private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ }
+
+ /** Creates a {@link GetByUriRequest.Builder} instance. */
+ public Builder(@NonNull String namespace) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ }
+
+ /**
* Sets the namespace to retrieve documents for.
*
- * <p>If this is not called, the namespace defaults to {@link
- * GenericDocument#DEFAULT_NAMESPACE}.
+ * <p>If this is not called, the namespace defaults to an empty string.
+ *
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
*
* @throws IllegalStateException if the builder has already been used.
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must
*/
+ @Deprecated
@NonNull
public Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 39b53b604abb..455cf3a26b50 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -59,17 +59,39 @@ public final class RemoveByUriRequest {
* <p>Once {@link #build} is called, the instance can no longer be used.
*/
public static final class Builder {
- private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ private String mNamespace;
private final Set<String> mUris = new ArraySet<>();
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ }
+
+ /** Creates a {@link RemoveByUriRequest.Builder} instance. */
+ public Builder(@NonNull String namespace) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ }
+
+ /**
* Sets the namespace to remove documents for.
*
- * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+ * <p>If this is not set, it defaults to an empty string.
+ *
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
*
* @throws IllegalStateException if the builder has already been used.
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must
*/
+ @Deprecated
@NonNull
public Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index 2bfcf2855430..2cd08c631006 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -62,18 +62,40 @@ public final class ReportUsageRequest {
/** Builder for {@link ReportUsageRequest} objects. */
public static final class Builder {
- private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ private String mNamespace;
private String mUri;
private Long mUsageTimeMillis;
private boolean mBuilt = false;
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ *
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ public Builder() {
+ mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+ }
+
+ /** Creates a {@link ReportUsageRequest.Builder} instance. */
+ public Builder(@NonNull String namespace) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ }
+
+ /**
* Sets which namespace the document being used belongs to.
*
- * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+ * <p>If this is not set, it defaults to an empty string.
+ *
+ * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+ * removed.
*
* @throws IllegalStateException if the builder has already been used
+ * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+ * exists only for dogfooder transition and must
*/
+ @Deprecated
@NonNull
public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index f34034b1c5c0..cb20849dd36f 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -32,7 +32,7 @@ import java.util.Objects;
* <p>This allows clients to obtain:
*
* <ul>
- * <li>The document which matched, using {@link #getDocument}
+ * <li>The document which matched, using {@link #getGenericDocument}
* <li>Information about which properties in the document matched, and "snippet" information
* containing textual summaries of the document's matches, using {@link #getMatches}
* </ul>
@@ -43,17 +43,10 @@ import java.util.Objects;
* @see SearchResults
*/
public final class SearchResult {
- /** @hide */
- public static final String DOCUMENT_FIELD = "document";
-
- /** @hide */
- public static final String MATCHES_FIELD = "matches";
-
- /** @hide */
- public static final String PACKAGE_NAME_FIELD = "packageName";
-
- /** @hide */
- public static final String DATABASE_NAME_FIELD = "databaseName";
+ static final String DOCUMENT_FIELD = "document";
+ static final String MATCHES_FIELD = "matches";
+ static final String PACKAGE_NAME_FIELD = "packageName";
+ static final String DATABASE_NAME_FIELD = "databaseName";
@NonNull private final Bundle mBundle;
@@ -74,13 +67,20 @@ public final class SearchResult {
return mBundle;
}
+ /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+ @NonNull
+ @Deprecated
+ public GenericDocument getDocument() {
+ return getGenericDocument();
+ }
+
/**
* Contains the matching {@link GenericDocument}.
*
* @return Document object which matched the query.
*/
@NonNull
- public GenericDocument getDocument() {
+ public GenericDocument getGenericDocument() {
if (mDocument == null) {
mDocument =
new GenericDocument(
@@ -104,7 +104,7 @@ public final class SearchResult {
Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD));
mMatches = new ArrayList<>(matchBundles.size());
for (int i = 0; i < matchBundles.size(); i++) {
- MatchInfo matchInfo = new MatchInfo(getDocument(), matchBundles.get(i));
+ MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument());
mMatches.add(matchInfo);
}
}
@@ -124,13 +124,69 @@ public final class SearchResult {
/**
* Contains the database name that stored the {@link GenericDocument}.
*
- * @return Database name that stored the document
+ * @return Name of the database within which the document is stored
*/
@NonNull
public String getDatabaseName() {
return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD));
}
+ /** Builder for {@link SearchResult} objects. */
+ public static final class Builder {
+ private final Bundle mBundle = new Bundle();
+ private final ArrayList<Bundle> mMatchInfos = new ArrayList<>();
+
+ private boolean mBuilt;
+
+ /**
+ * Constructs a new builder for {@link SearchResult} objects.
+ *
+ * @param packageName the package name the matched document belongs to
+ * @param databaseName the database name the matched document belongs to.
+ */
+ public Builder(@NonNull String packageName, @NonNull String databaseName) {
+ mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName));
+ mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName));
+ }
+
+ /**
+ * Sets the document which matched.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setGenericDocument(@NonNull GenericDocument document) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putBundle(DOCUMENT_FIELD, document.getBundle());
+ return this;
+ }
+
+ /** Adds another match to this SearchResult. */
+ @NonNull
+ public Builder addMatch(@NonNull MatchInfo matchInfo) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkState(
+ matchInfo.mDocument == null,
+ "This MatchInfo is already associated with a SearchResult and can't be "
+ + "reassigned");
+ mMatchInfos.add(matchInfo.mBundle);
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link SearchResult}.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public SearchResult build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putParcelableArrayList(MATCHES_FIELD, mMatchInfos);
+ mBuilt = true;
+ return new SearchResult(mBundle);
+ }
+ }
+
/**
* This class represents a match objects for any Snippets that might be present in {@link
* SearchResults} from query. Using this class user can get the full text, exact matches and
@@ -147,11 +203,11 @@ public final class SearchResult {
* <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
* nonsense word that’s used a lot is bar."
*
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
+ * <p>{@link MatchInfo#getExactMatchRange()} returns [29, 32]
*
* <p>{@link MatchInfo#getExactMatch()} returns "foo"
*
- * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33]
+ * <p>{@link MatchInfo#getSnippetRange()} returns [26, 33]
*
* <p>{@link MatchInfo#getSnippet()} returns "is foo."
*
@@ -172,11 +228,11 @@ public final class SearchResult {
*
* <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
*
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
+ * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 4]
*
* <p>{@link MatchInfo#getExactMatch()} returns "Test"
*
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
+ * <p>{@link MatchInfo#getSnippetRange()} returns [0, 9]
*
* <p>{@link MatchInfo#getSnippet()} returns "Test Name"
*
@@ -186,52 +242,54 @@ public final class SearchResult {
*
* <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
*
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
+ * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 20]
*
* <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
*
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
+ * <p>{@link MatchInfo#getSnippetRange()} returns [0, 20]
*
* <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
*/
public static final class MatchInfo {
+ /** The path of the matching snippet property. */
+ private static final String PROPERTY_PATH_FIELD = "propertyPath";
+
+ private static final String EXACT_MATCH_RANGE_LOWER_FIELD = "exactMatchRangeLower";
+ private static final String EXACT_MATCH_RANGE_UPPER_FIELD = "exactMatchRangeUpper";
+ private static final String SNIPPET_RANGE_LOWER_FIELD = "snippetRangeLower";
+ private static final String SNIPPET_RANGE_UPPER_FIELD = "snippetRangeUpper";
+
+ private final String mPropertyPath;
+ final Bundle mBundle;
+
/**
- * The path of the matching snippet property.
+ * Document which the match comes from.
*
- * @hide
+ * <p>If this is {@code null}, methods which require access to the document, like {@link
+ * #getExactMatch}, will throw {@link NullPointerException}.
*/
- public static final String PROPERTY_PATH_FIELD = "propertyPath";
+ @Nullable final GenericDocument mDocument;
- /** @hide */
- public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
+ /** Full text of the matched property. Populated on first use. */
+ @Nullable private String mFullText;
- /** @hide */
- public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper";
+ /** Range of property that exactly matched the query. Populated on first use. */
+ @Nullable private MatchRange mExactMatchRange;
- /** @hide */
- public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower";
+ /** Range of some reasonable amount of context around the query. Populated on first use. */
+ @Nullable private MatchRange mWindowRange;
- /** @hide */
- public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper";
-
- private final String mFullText;
- private final String mPropertyPath;
- private final Bundle mBundle;
- private MatchRange mExactMatchRange;
- private MatchRange mWindowRange;
-
- MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) {
+ MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) {
mBundle = Preconditions.checkNotNull(bundle);
- Preconditions.checkNotNull(document);
+ mDocument = document;
mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
- mFullText = getPropertyValues(document, mPropertyPath);
}
/**
* Gets the property path corresponding to the given entry.
*
- * <p>Property Path: '.' - delimited sequence of property names indicating which property in
- * the Document these snippets correspond to.
+ * <p>A property path is a '.' - delimited sequence of property names indicating which
+ * property in the document these snippets correspond to.
*
* <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
* example 1 this returns "subject"
@@ -249,21 +307,34 @@ public final class SearchResult {
*/
@NonNull
public String getFullText() {
+ if (mFullText == null) {
+ Preconditions.checkState(
+ mDocument != null,
+ "Document has not been populated; this MatchInfo cannot be used yet");
+ mFullText = getPropertyValues(mDocument, mPropertyPath);
+ }
return mFullText;
}
+ /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+ @NonNull
+ @Deprecated
+ public MatchRange getExactMatchPosition() {
+ return getExactMatchRange();
+ }
+
/**
* Gets the exact {@link MatchRange} corresponding to the given entry.
*
* <p>For class example 1 this returns [29, 32]
*/
@NonNull
- public MatchRange getExactMatchPosition() {
+ public MatchRange getExactMatchRange() {
if (mExactMatchRange == null) {
mExactMatchRange =
new MatchRange(
- mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
- mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
+ mBundle.getInt(EXACT_MATCH_RANGE_LOWER_FIELD),
+ mBundle.getInt(EXACT_MATCH_RANGE_UPPER_FIELD));
}
return mExactMatchRange;
}
@@ -275,7 +346,14 @@ public final class SearchResult {
*/
@NonNull
public CharSequence getExactMatch() {
- return getSubstring(getExactMatchPosition());
+ return getSubstring(getExactMatchRange());
+ }
+
+ /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+ @NonNull
+ @Deprecated
+ public MatchRange getSnippetPosition() {
+ return getSnippetRange();
}
/**
@@ -287,12 +365,12 @@ public final class SearchResult {
* <p>For class example 1 this returns [29, 41].
*/
@NonNull
- public MatchRange getSnippetPosition() {
+ public MatchRange getSnippetRange() {
if (mWindowRange == null) {
mWindowRange =
new MatchRange(
- mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
- mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
+ mBundle.getInt(SNIPPET_RANGE_LOWER_FIELD),
+ mBundle.getInt(SNIPPET_RANGE_UPPER_FIELD));
}
return mWindowRange;
}
@@ -309,7 +387,7 @@ public final class SearchResult {
*/
@NonNull
public CharSequence getSnippet() {
- return getSubstring(getSnippetPosition());
+ return getSubstring(getSnippetRange());
}
private CharSequence getSubstring(MatchRange range) {
@@ -331,6 +409,72 @@ public final class SearchResult {
// TODO(b/175146044): Return the proper match based on the index in the propertyName.
return values[0];
}
+
+ /** Builder for {@link MatchInfo} objects. */
+ public static final class Builder {
+ private final Bundle mBundle = new Bundle();
+ private boolean mBuilt = false;
+
+ /**
+ * Sets the property path corresponding to the given entry.
+ *
+ * <p>A property path is a '.' - delimited sequence of property names indicating which
+ * property in the document these snippets correspond to.
+ *
+ * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
+ * example 1 this returns "subject"
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setPropertyPath(@NonNull String propertyPath) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putString(
+ SearchResult.MatchInfo.PROPERTY_PATH_FIELD,
+ Preconditions.checkNotNull(propertyPath));
+ return this;
+ }
+
+ /**
+ * Sets the exact {@link MatchRange} corresponding to the given entry.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setExactMatchRange(@NonNull MatchRange matchRange) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(matchRange);
+ mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart());
+ mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd());
+ return this;
+ }
+
+ /**
+ * Sets the snippet {@link MatchRange} corresponding to the given entry.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public Builder setSnippetRange(@NonNull MatchRange matchRange) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(matchRange);
+ mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart());
+ mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd());
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link MatchInfo}.
+ *
+ * @throws IllegalStateException if the builder has already been used
+ */
+ @NonNull
+ public MatchInfo build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBuilt = true;
+ return new MatchInfo(mBundle, /*document=*/ null);
+ }
+ }
}
/**
@@ -353,7 +497,6 @@ public final class SearchResult {
*
* @param start The start point (inclusive)
* @param end The end point (exclusive)
- * @hide
*/
public MatchRange(int start, int end) {
if (start > end) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index a146006f355c..98cd49b79737 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -160,12 +160,8 @@ public class SetSchemaResponse {
.addMigrationFailures(mMigrationFailures);
}
- /**
- * Builder for {@link SetSchemaResponse} objects.
- *
- * @hide
- */
- public static class Builder {
+ /** Builder for {@link SetSchemaResponse} objects. */
+ public static final class Builder {
private final ArrayList<MigrationFailure> mMigrationFailures = new ArrayList<>();
private final ArrayList<String> mDeletedTypes = new ArrayList<>();
private final ArrayList<String> mMigratedTypes = new ArrayList<>();
@@ -309,12 +305,8 @@ public class SetSchemaResponse {
mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
}
- /**
- * Builder for {@link MigrationFailure} objects.
- *
- * @hide
- */
- public static class Builder {
+ /** Builder for {@link MigrationFailure} objects. */
+ public static final class Builder {
private String mSchemaType;
private String mNamespace;
private String mUri;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
index b1a33a478a47..ca4ea2bfd3bb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
@@ -32,19 +32,27 @@ public class AppSearchException extends Exception {
/**
* Initializes an {@link AppSearchException} with no message.
*
- * @hide
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
*/
public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
this(resultCode, /*message=*/ null);
}
- /** @hide */
+ /**
+ * Initializes an {@link AppSearchException} with a result code and message.
+ *
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ */
public AppSearchException(
@AppSearchResult.ResultCode int resultCode, @Nullable String message) {
this(resultCode, message, /*cause=*/ null);
}
- /** @hide */
+ /**
+ * Initializes an {@link AppSearchException} with a result code, message and cause.
+ *
+ * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ */
public AppSearchException(
@AppSearchResult.ResultCode int resultCode,
@Nullable String message,
@@ -53,12 +61,16 @@ public class AppSearchException extends Exception {
mResultCode = resultCode;
}
- /** Returns the result code this exception was constructed with. */
+ /**
+ * Returns the result code this exception was constructed with.
+ *
+ * @return One of the constants documented in {@link AppSearchResult#getResultCode}.
+ */
public @AppSearchResult.ResultCode int getResultCode() {
return mResultCode;
}
- /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */
+ /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}. */
@NonNull
public <T> AppSearchResult<T> toAppSearchResult() {
return AppSearchResult.newFailedResult(mResultCode, getMessage());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
index a7f1cc4c793f..4b8ce6d2c1d1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
@@ -93,7 +93,7 @@ class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper {
migrator.transform(
currentVersion,
finalVersion,
- searchResultPage.getResults().get(i).getDocument());
+ searchResultPage.getResults().get(i).getGenericDocument());
Bundle bundle = newDocument.getBundle();
Parcel parcel = Parcel.obtain();
parcel.writeBundle(bundle);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index a2386eccc256..d6b9da827515 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -102,8 +102,8 @@ public final class GenericDocumentToProtoConverter {
public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) {
Preconditions.checkNotNull(proto);
GenericDocument.Builder<?> documentBuilder =
- new GenericDocument.Builder<>(proto.getUri(), proto.getSchema())
- .setNamespace(proto.getNamespace())
+ new GenericDocument.Builder<>(
+ proto.getNamespace(), proto.getUri(), proto.getSchema())
.setScore(proto.getScore())
.setTtlMillis(proto.getTtlMs())
.setCreationTimestampMillis(proto.getCreationTimestampMs());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index e9852aa1cd41..1d8db7233a7a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -58,14 +58,14 @@ public class SearchResultToProtoConverter {
@NonNull List<String> databaseNames) {
Preconditions.checkArgument(
proto.getResultsCount() == packageNames.size(),
- "Size of " + "results does not match the number of package names.");
+ "Size of results does not match the number of package names.");
Bundle bundle = new Bundle();
bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken());
ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
for (int i = 0; i < proto.getResultsCount(); i++) {
- resultBundles.add(
- toSearchResultBundle(
- proto.getResults(i), packageNames.get(i), databaseNames.get(i)));
+ SearchResult result =
+ toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i));
+ resultBundles.add(result.getBundle());
}
bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
return new SearchResultPage(bundle);
@@ -80,50 +80,41 @@ public class SearchResultToProtoConverter {
* @return A {@link SearchResult} bundle.
*/
@NonNull
- private static Bundle toSearchResultBundle(
+ private static SearchResult toSearchResult(
@NonNull SearchResultProto.ResultProtoOrBuilder proto,
@NonNull String packageName,
@NonNull String databaseName) {
- Bundle bundle = new Bundle();
GenericDocument document =
GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument());
- bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle());
- bundle.putString(SearchResult.PACKAGE_NAME_FIELD, packageName);
- bundle.putString(SearchResult.DATABASE_NAME_FIELD, databaseName);
-
- ArrayList<Bundle> matchList = new ArrayList<>();
+ SearchResult.Builder builder =
+ new SearchResult.Builder(packageName, databaseName).setGenericDocument(document);
if (proto.hasSnippet()) {
for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
for (int j = 0; j < entry.getSnippetMatchesCount(); j++) {
- Bundle matchInfoBundle =
- convertToMatchInfoBundle(
- entry.getSnippetMatches(j), entry.getPropertyName());
- matchList.add(matchInfoBundle);
+ SearchResult.MatchInfo matchInfo =
+ toMatchInfo(entry.getSnippetMatches(j), entry.getPropertyName());
+ builder.addMatch(matchInfo);
}
}
}
- bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList);
-
- return bundle;
+ return builder.build();
}
- private static Bundle convertToMatchInfoBundle(
- SnippetMatchProto snippetMatchProto, String propertyPath) {
- Bundle bundle = new Bundle();
- bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
- bundle.putInt(
- SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
- snippetMatchProto.getExactMatchPosition());
- bundle.putInt(
- SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD,
- snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes());
- bundle.putInt(
- SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD,
- snippetMatchProto.getWindowPosition());
- bundle.putInt(
- SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD,
- snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes());
- return bundle;
+ private static SearchResult.MatchInfo toMatchInfo(
+ @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) {
+ return new SearchResult.MatchInfo.Builder()
+ .setPropertyPath(propertyPath)
+ .setExactMatchRange(
+ new SearchResult.MatchRange(
+ snippetMatchProto.getExactMatchPosition(),
+ snippetMatchProto.getExactMatchPosition()
+ + snippetMatchProto.getExactMatchBytes()))
+ .setSnippetRange(
+ new SearchResult.MatchRange(
+ snippetMatchProto.getWindowPosition(),
+ snippetMatchProto.getWindowPosition()
+ + snippetMatchProto.getWindowBytes()))
+ .build();
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
new file mode 100644
index 000000000000..aeb66d90b5be
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.stats;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.appsearch.external.localstorage.AppSearchLogger;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Logger Implementation using Westworld.
+ *
+ * <p>This class is thread-safe.
+ *
+ * @hide
+ */
+public final class PlatformLogger implements AppSearchLogger {
+ private static final String TAG = "AppSearchPlatformLogger";
+
+ // Context of the system service.
+ private final Context mContext;
+
+ // User ID of the caller who we're logging for.
+ private final int mUserId;
+
+ // Configuration for the logger
+ private final Config mConfig;
+
+ private final Random mRng = new Random();
+ private final Object mLock = new Object();
+
+ /**
+ * SparseArray to track how many stats we skipped due to
+ * {@link Config#mMinTimeIntervalBetweenSamplesMillis}.
+ *
+ * <p> We can have correct extrapolated number by adding those counts back when we log
+ * the same type of stats next time. E.g. the true count of an event could be estimated as:
+ * SUM(sampling_ratio * (num_skipped_sample + 1)) as est_count
+ *
+ * <p>The key to the SparseArray is {@link CallStats.CallType}
+ */
+ @GuardedBy("mLock")
+ private final SparseIntArray mSkippedSampleCountLocked =
+ new SparseIntArray();
+
+ /**
+ * Map to cache the packageUid for each package.
+ *
+ * <p>It maps packageName to packageUid.
+ *
+ * <p>The entry will be removed whenever the app gets uninstalled
+ */
+ @GuardedBy("mLock")
+ private final Map<String, Integer> mPackageUidCacheLocked =
+ new ArrayMap<>();
+
+ /**
+ * Elapsed time for last stats logged from boot in millis
+ */
+ @GuardedBy("mLock")
+ private long mLastPushTimeMillisLocked = 0;
+
+ /**
+ * Class to configure the {@link PlatformLogger}
+ */
+ public static final class Config {
+ // Minimum time interval (in millis) since last message logged to Westworld before
+ // logging again.
+ private final long mMinTimeIntervalBetweenSamplesMillis;
+
+ // Default sampling ratio for all types of stats
+ private final int mDefaultSamplingRatio;
+
+ /**
+ * Sampling ratios for different types of stats
+ *
+ * <p>This SparseArray is passed by client and is READ-ONLY. The key to that SparseArray is
+ * {@link CallStats.CallType}
+ *
+ * <p>If sampling ratio is missing for certain stats type,
+ * {@link Config#mDefaultSamplingRatio} will be used.
+ *
+ * <p>E.g. sampling ratio=10 means that one out of every 10 stats was logged. If sampling
+ * ratio is 1, we will log each sample and it acts as if the sampling is disabled.
+ */
+ @NonNull
+ private final SparseIntArray mSamplingRatios;
+
+ /**
+ * Configuration for {@link PlatformLogger}
+ *
+ * @param minTimeIntervalBetweenSamplesMillis minimum time interval apart in Milliseconds
+ * required for two consecutive stats logged
+ * @param defaultSamplingRatio default sampling ratio
+ * @param samplingRatios SparseArray to customize sampling ratio for
+ * different stat types
+ */
+ public Config(long minTimeIntervalBetweenSamplesMillis,
+ int defaultSamplingRatio,
+ @Nullable SparseIntArray samplingRatios) {
+ mMinTimeIntervalBetweenSamplesMillis = minTimeIntervalBetweenSamplesMillis;
+ mDefaultSamplingRatio = defaultSamplingRatio;
+ if (samplingRatios != null) {
+ mSamplingRatios = samplingRatios;
+ } else {
+ mSamplingRatios = new SparseIntArray();
+ }
+ }
+ }
+
+ /**
+ * Helper class to hold platform specific stats for Westworld.
+ */
+ static final class ExtraStats {
+ // UID for the calling package of the stats.
+ final int mPackageUid;
+ // sampling ratio for the call type of the stats.
+ final int mSamplingRatio;
+ // number of samplings skipped before the current one for the same call type.
+ final int mSkippedSampleCount;
+
+ ExtraStats(int packageUid, int samplingRatio, int skippedSampleCount) {
+ mPackageUid = packageUid;
+ mSamplingRatio = samplingRatio;
+ mSkippedSampleCount = skippedSampleCount;
+ }
+ }
+
+ /**
+ * Westworld constructor
+ */
+ public PlatformLogger(@NonNull Context context, int userId, @NonNull Config config) {
+ mContext = Preconditions.checkNotNull(context);
+ mConfig = Preconditions.checkNotNull(config);
+ mUserId = userId;
+ }
+
+ /** Logs {@link CallStats}. */
+ @Override
+ public void logStats(@NonNull CallStats stats) {
+ Preconditions.checkNotNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(stats.getCallType())) {
+ logToWestworldLocked(stats);
+ }
+ }
+ }
+
+ /** Logs {@link PutDocumentStats}. */
+ @Override
+ public void logStats(@NonNull PutDocumentStats stats) {
+ Preconditions.checkNotNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) {
+ logToWestworldLocked(stats);
+ }
+ }
+ }
+
+ /**
+ * Removes cached UID for package.
+ *
+ * @return removed UID for the package, or {@code INVALID_UID} if package was not previously
+ * cached.
+ */
+ public int removeCachedUidForPackage(@NonNull String packageName) {
+ // TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent
+ Preconditions.checkNotNull(packageName);
+ synchronized (mLock) {
+ Integer uid = mPackageUidCacheLocked.remove(packageName);
+ return uid != null ? uid : Process.INVALID_UID;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void logToWestworldLocked(@NonNull CallStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
+ stats.getCallType());
+ /* TODO(b/173532925) Log the CallStats to Westworld
+ stats.log(..., samplingRatio, skippedSampleCount, ...)
+ */
+ }
+
+ @GuardedBy("mLock")
+ private void logToWestworldLocked(@NonNull PutDocumentStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+ /* TODO(b/173532925) Log the PutDocumentStats to Westworld
+ stats.log(..., samplingRatio, skippedSampleCount, ...)
+ */
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ @NonNull
+ ExtraStats createExtraStatsLocked(@NonNull String packageName,
+ @CallStats.CallType int callType) {
+ int packageUid = getPackageUidAsUserLocked(packageName);
+ int samplingRatio = mConfig.mSamplingRatios.get(callType,
+ mConfig.mDefaultSamplingRatio);
+
+ int skippedSampleCount = mSkippedSampleCountLocked.get(callType,
+ /*valueOfKeyIfNotFound=*/ 0);
+ mSkippedSampleCountLocked.put(callType, 0);
+
+ return new ExtraStats(packageUid, samplingRatio, skippedSampleCount);
+ }
+
+ /**
+ * Checks if this stats should be logged.
+ *
+ * <p>It won't be logged if it is "sampled" out, or it is too close to the previous logged
+ * stats.
+ */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ boolean shouldLogForTypeLocked(@CallStats.CallType int callType) {
+ int samplingRatio = mConfig.mSamplingRatios.get(callType,
+ mConfig.mDefaultSamplingRatio);
+
+ // Sampling
+ if (!shouldSample(samplingRatio)) {
+ return false;
+ }
+
+ // Rate limiting
+ // Check the timestamp to see if it is too close to last logged sample
+ long currentTimeMillis = SystemClock.elapsedRealtime();
+ if (mLastPushTimeMillisLocked
+ > currentTimeMillis - mConfig.mMinTimeIntervalBetweenSamplesMillis) {
+ int count = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0);
+ ++count;
+ mSkippedSampleCountLocked.put(callType, count);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if the stats should be "sampled"
+ *
+ * @param samplingRatio sampling ratio
+ * @return if the stats should be sampled
+ */
+ private boolean shouldSample(int samplingRatio) {
+ if (samplingRatio <= 0) {
+ return false;
+ }
+
+ return mRng.nextInt((int) samplingRatio) == 0;
+ }
+
+ /**
+ * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
+ * find the UID.
+ */
+ @GuardedBy("mLock")
+ private int getPackageUidAsUserLocked(@NonNull String packageName) {
+ Integer packageUid = mPackageUidCacheLocked.get(packageName);
+ if (packageUid != null) {
+ return packageUid;
+ }
+
+ // TODO(b/173532925) since VisibilityStore has the same method, we can make this a
+ // utility function
+ try {
+ packageUid = mContext.getPackageManager().getPackageUidAsUser(packageName, mUserId);
+ mPackageUidCacheLocked.put(packageName, packageUid);
+ return packageUid;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package doesn't exist, continue
+ }
+ return Process.INVALID_UID;
+ }
+
+ //
+ // Functions below are used for tests only
+ //
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void setLastPushTimeMillisLocked(long lastPushElapsedTimeMillis) {
+ mLastPushTimeMillisLocked = lastPushElapsedTimeMillis;
+ }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 41c70f072262..68531b6ee1e5 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I42b89416968565ceb6483b400894f5b49524208c
+I1926fb1d13628607f7a513c8149b65dd86c98dd6
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 37717d6837b9..4a3c7a53d43e 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -47,10 +47,7 @@ public class AppSearchTestUtils {
AppSearchBatchResult<String, GenericDocument> result =
checkIsBatchResultSuccess(
session.getByUri(
- new GetByUriRequest.Builder()
- .setNamespace(namespace)
- .addUris(uris)
- .build()));
+ new GetByUriRequest.Builder(namespace).addUris(uris).build()));
assertThat(result.getSuccesses()).hasSize(uris.length);
assertThat(result.getFailures()).isEmpty();
List<GenericDocument> list = new ArrayList<>(uris.length);
@@ -80,7 +77,7 @@ public class AppSearchTestUtils {
List<GenericDocument> documents = new ArrayList<>();
while (results.size() > 0) {
for (SearchResult result : results) {
- documents.add(result.getDocument());
+ documents.add(result.getGenericDocument());
}
results = searchResults.getNextPage().get();
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
index 4b0f719b13be..999860fdf4da 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
@@ -42,7 +42,7 @@ public class BlobStoreIdleJobService extends JobService {
blobStoreManagerInternal.onIdleMaintenance();
jobFinished(params, false);
});
- return false;
+ return true;
}
@Override
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 77146e0d1282..78c5b156bc45 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -539,6 +539,11 @@ public class AlarmManager {
* scheduled as exact. Applications are strongly discouraged from using exact
* alarms unnecessarily as they reduce the OS's ability to minimize battery use.
*
+ * <p>
+ * Starting with {@link Build.VERSION_CODES#S}, apps require the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this
+ * API.
+ *
* @param type type of alarm.
* @param triggerAtMillis time in milliseconds that the alarm should go
* off, using the appropriate clock (depending on the alarm type).
@@ -558,6 +563,7 @@ public class AlarmManager {
* @see #RTC
* @see #RTC_WAKEUP
*/
+ @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
null, null);
@@ -571,7 +577,13 @@ public class AlarmManager {
* The OnAlarmListener's {@link OnAlarmListener#onAlarm() onAlarm()} method will be
* invoked via the specified target Handler, or on the application's main looper
* if {@code null} is passed as the {@code targetHandler} parameter.
+ *
+ * <p>
+ * Starting with {@link Build.VERSION_CODES#S}, apps require the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM SCHEDULE_EXACT_ALARM} permission to use this
+ * API.
*/
+ @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExact(@AlarmType int type, long triggerAtMillis, String tag,
OnAlarmListener listener, Handler targetHandler) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag,
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index b7a3f1083176..4c8ab9385903 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -59,12 +59,6 @@ import java.util.Objects;
* constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an
* exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is
* valid to schedule jobs with no constraints.
- * <p> In Android version {@link Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum execution time
- * of one minute. Starting with Android version {@link Build.VERSION_CODES#M} and ending with
- * Android version {@link Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes.
- * Starting from Android version {@link Build.VERSION_CODES#S}, jobs will still be stopped after
- * 10 minutes if the system is busy or needs the resources, but if not, jobs may continue running
- * longer than 10 minutes.
*/
public class JobInfo implements Parcelable {
private static String TAG = "JobInfo";
@@ -1468,7 +1462,7 @@ public class JobInfo implements Parcelable {
* <ol>
* <li>Run as soon as possible</li>
* <li>Be less restricted during Doze and battery saver</li>
- * <li>Have network access</li>
+ * <li>Have the same network access as foreground services</li>
* <li>Be less likely to be killed than regular jobs</li>
* <li>Be subject to background location throttling</li>
* </ol>
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 361325dae7fd..1f4ef0470ebd 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -57,6 +57,19 @@ import java.util.List;
* {@link android.content.Context#getSystemService
* Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.
*
+ * <p> Prior to Android version {@link android.os.Build.VERSION_CODES#S}, jobs could only have
+ * a maximum of 100 jobs scheduled at a time. Starting with Android version
+ * {@link android.os.Build.VERSION_CODES#S}, that limit has been increased to 150.
+ * Expedited jobs also count towards the limit.
+ *
+ * <p> In Android version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum
+ * execution time of one minute. Starting with Android version
+ * {@link android.os.Build.VERSION_CODES#M} and ending with Android version
+ * {@link android.os.Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes.
+ * Starting from Android version {@link android.os.Build.VERSION_CODES#S}, jobs will still be
+ * stopped after 10 minutes if the system is busy or needs the resources, but if not, jobs
+ * may continue running longer than 10 minutes.
+ *
* <p class="caution"><strong>Note:</strong> Beginning with API 30
* ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications.
* Calling {@link #schedule(JobInfo)} and other such methods with very high frequency can have a
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
index 0c4fcb4ec1b0..6e4a5a0c5784 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
@@ -22,6 +22,7 @@ import android.app.SystemServiceRegistry;
import android.content.Context;
import android.os.DeviceIdleManager;
import android.os.IDeviceIdleController;
+import android.os.PowerExemptionManager;
import android.os.PowerWhitelistManager;
/**
@@ -52,5 +53,8 @@ public class JobSchedulerFrameworkInitializer {
SystemServiceRegistry.registerContextAwareService(
Context.POWER_WHITELIST_MANAGER, PowerWhitelistManager.class,
PowerWhitelistManager::new);
+ SystemServiceRegistry.registerContextAwareService(
+ Context.POWER_EXEMPTION_SERVICE, PowerExemptionManager.class,
+ PowerExemptionManager::new);
}
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index 61afadab9b0c..0f3d299291c5 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -74,6 +74,7 @@ public abstract class JobService extends Service {
/**
* Call this to inform the JobScheduler that the job has finished its work. When the
* system receives this message, it releases the wakelock being held for the job.
+ * This does not need to be called if {@link #onStopJob(JobParameters)} has been called.
* <p>
* You can request that the job be scheduled again by passing {@code true} as
* the <code>wantsReschedule</code> parameter. This will apply back-off policy
@@ -135,6 +136,8 @@ public abstract class JobService extends Service {
/**
* This method is called if the system has determined that you must stop execution of your job
* even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}.
+ * Once this method is called, you no longer need to call
+ * {@link #jobFinished(JobParameters, boolean)}.
*
* <p>This will happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
@@ -144,8 +147,8 @@ public abstract class JobService extends Service {
* idle maintenance window. You are solely responsible for the behavior of your application
* upon receipt of this message; your app will likely start to misbehave if you ignore it.
* <p>
- * Once this method returns, the system releases the wakelock that it is holding on
- * behalf of the job.</p>
+ * Once this method returns (or times out), the system releases the wakelock that it is holding
+ * on behalf of the job.</p>
*
* @param params The parameters identifying this job, as supplied to
* the job in the {@link #onStartJob(JobParameters)} callback.
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 752c36e53bf9..6cdf5853339a 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -65,7 +65,7 @@ public class DeviceIdleManager {
* @return package names the system has white-listed to opt out of power save restrictions,
* except for device idle mode.
*
- * @hide Should be migrated to PowerWhitelistManager
+ * @hide Should be migrated to PowerExemptionManager
*/
@TestApi
public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
@@ -80,7 +80,7 @@ public class DeviceIdleManager {
* @return package names the system has white-listed to opt out of power save restrictions for
* all modes.
*
- * @hide Should be migrated to PowerWhitelistManager
+ * @hide Should be migrated to PowerExemptionManager
*/
@TestApi
public @NonNull String[] getSystemPowerWhitelist() {
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 43d4873a3540..9d18dfe98a34 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -42,7 +42,7 @@ interface IDeviceIdleController {
boolean isPowerSaveWhitelistExceptIdleApp(String name);
boolean isPowerSaveWhitelistApp(String name);
@UnsupportedAppUsage(maxTargetSdk = 30,
- publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.")
+ publicAlternatives = "Use SystemApi {@code PowerExemptionManager#addToTemporaryAllowList(String, int, int, String)}.")
void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason);
long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason);
long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason);
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
new file mode 100644
index 000000000000..88f21a53f930
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Interface to access and modify the permanent and temporary power save allow list. The two lists
+ * are kept separately. Apps placed on the permanent allow list are only removed via an explicit
+ * {@link #removeFromAllowList(String)} call. Apps allow-listed by default by the system cannot be
+ * removed. Apps placed on the temporary allow list are removed from that allow list after a
+ * predetermined amount of time.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.POWER_EXEMPTION_SERVICE)
+public class PowerExemptionManager {
+ private final Context mContext;
+ // Proxy to DeviceIdleController for now
+ // TODO: migrate to PowerExemptionController
+ private final IDeviceIdleController mService;
+
+ /**
+ * Indicates that an unforeseen event has occurred and the app should be allow-listed to handle
+ * it.
+ */
+ public static final int EVENT_UNSPECIFIED = 0;
+
+ /**
+ * Indicates that an SMS event has occurred and the app should be allow-listed to handle it.
+ */
+ public static final int EVENT_SMS = 1;
+
+ /**
+ * Indicates that an MMS event has occurred and the app should be allow-listed to handle it.
+ */
+ public static final int EVENT_MMS = 2;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"EVENT_"}, value = {
+ EVENT_UNSPECIFIED,
+ EVENT_SMS,
+ EVENT_MMS,
+ })
+ public @interface AllowListEvent {
+ }
+
+ /**
+ * Allow the temp allow list behavior, plus allow foreground service start from background.
+ */
+ public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+ /**
+ * Only allow the temp allow list behavior, not allow foreground service start from background.
+ */
+ public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+
+ /**
+ * The list of temp allow list types.
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_LIST_TYPE_" }, value = {
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TempAllowListType {}
+
+ /* Reason codes for BG-FGS-launch. */
+ /**
+ * BG-FGS-launch is denied.
+ * @hide
+ */
+ public static final int REASON_DENIED = -1;
+
+ /* Reason code range 0-9 are reserved for default reasons */
+ /**
+ * The default reason code if reason is unknown.
+ */
+ public static final int REASON_UNKNOWN = 0;
+ /**
+ * Use REASON_OTHER if there is no better choice.
+ */
+ public static final int REASON_OTHER = 1;
+
+ /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
+ /** @hide */
+ public static final int REASON_PROC_STATE_PERSISTENT = 10;
+ /** @hide */
+ public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+ /** @hide */
+ public static final int REASON_PROC_STATE_TOP = 12;
+ /** @hide */
+ public static final int REASON_PROC_STATE_BTOP = 13;
+ /** @hide */
+ public static final int REASON_PROC_STATE_FGS = 14;
+ /** @hide */
+ public static final int REASON_PROC_STATE_BFGS = 15;
+
+ /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
+ /** @hide */
+ public static final int REASON_UID_VISIBLE = 50;
+ /** @hide */
+ public static final int REASON_SYSTEM_UID = 51;
+ /** @hide */
+ public static final int REASON_ACTIVITY_STARTER = 52;
+ /** @hide */
+ public static final int REASON_START_ACTIVITY_FLAG = 53;
+ /** @hide */
+ public static final int REASON_FGS_BINDING = 54;
+ /** @hide */
+ public static final int REASON_DEVICE_OWNER = 55;
+ /** @hide */
+ public static final int REASON_PROFILE_OWNER = 56;
+ /** @hide */
+ public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+ /**
+ * START_ACTIVITIES_FROM_BACKGROUND permission.
+ * @hide
+ */
+ public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+ /**
+ * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
+ * @hide
+ */
+ public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+ /** @hide */
+ public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+ /** @hide */
+ public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+ /** @hide */
+ public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+ /** @hide */
+ public static final int REASON_DEVICE_DEMO_MODE = 63;
+ /** @hide */
+ public static final int REASON_EXEMPTED_PACKAGE = 64;
+ /** @hide */
+ public static final int REASON_ALLOWLISTED_PACKAGE = 65;
+ /** @hide */
+ public static final int REASON_APPOP = 66;
+ /** @hide */
+ public static final int REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD = 67;
+
+ /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list.
+ Reason code for temp and system allow list starts here.
+ Reason code range 100-199 are reserved for public reasons. */
+ /**
+ * Set temp-allow-list for location geofence purpose.
+ */
+ public static final int REASON_GEOFENCING = 100;
+ /**
+ * Set temp-allow-list for server push messaging.
+ */
+ public static final int REASON_PUSH_MESSAGING = 101;
+ /**
+ * Set temp-allow-list for server push messaging over the quota.
+ */
+ public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+ /**
+ * Set temp-allow-list for activity recognition.
+ */
+ public static final int REASON_ACTIVITY_RECOGNITION = 103;
+ /**
+ * Set temp-allow-list for transferring accounts between users.
+ */
+ public static final int REASON_ACCOUNT_TRANSFER = 104;
+
+ /* Reason code range 200-299 are reserved for broadcast actions */
+ /**
+ * Broadcast ACTION_BOOT_COMPLETED.
+ * @hide
+ */
+ public static final int REASON_BOOT_COMPLETED = 200;
+ /**
+ * Broadcast ACTION_PRE_BOOT_COMPLETED.
+ * @hide
+ */
+ public static final int REASON_PRE_BOOT_COMPLETED = 201;
+ /**
+ * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
+ * @hide
+ */
+ public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+
+ /* Reason code range 300-399 are reserved for other internal reasons */
+ /**
+ * Device idle system allow list, including EXCEPT-IDLE
+ * @hide
+ */
+ public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
+ /** @hide */
+ public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+ /**
+ * AlarmManagerService.
+ * @hide
+ */
+ public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+ /**
+ * ActiveServices.
+ * @hide
+ */
+ public static final int REASON_SERVICE_LAUNCH = 303;
+ /**
+ * KeyChainSystemService.
+ * @hide
+ */
+ public static final int REASON_KEY_CHAIN = 304;
+ /**
+ * PackageManagerService.
+ * @hide
+ */
+ public static final int REASON_PACKAGE_VERIFIER = 305;
+ /**
+ * SyncManager.
+ * @hide
+ */
+ public static final int REASON_SYNC_MANAGER = 306;
+ /**
+ * DomainVerificationProxyV1.
+ * @hide
+ */
+ public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+ /**
+ * DomainVerificationProxyV2.
+ * @hide
+ */
+ public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+ /** @hide */
+ public static final int REASON_VPN = 309;
+ /**
+ * NotificationManagerService.
+ * @hide
+ */
+ public static final int REASON_NOTIFICATION_SERVICE = 310;
+ /**
+ * Broadcast ACTION_MY_PACKAGE_REPLACED.
+ * @hide
+ */
+ public static final int REASON_PACKAGE_REPLACED = 311;
+ /**
+ * LocationProviderManager.
+ * @hide
+ */
+ public static final int REASON_LOCATION_PROVIDER = 312;
+ /**
+ * MediaButtonReceiver.
+ * @hide
+ */
+ public static final int REASON_MEDIA_BUTTON = 313;
+ /**
+ * InboundSmsHandler.
+ * @hide
+ */
+ public static final int REASON_EVENT_SMS = 314;
+ /**
+ * InboundSmsHandler.
+ * @hide
+ */
+ public static final int REASON_EVENT_MMS = 315;
+ /**
+ * Shell app.
+ * @hide
+ */
+ public static final int REASON_SHELL = 316;
+ /**
+ * Media session callbacks.
+ * @hide
+ */
+ public static final int REASON_MEDIA_SESSION_CALLBACK = 317;
+
+ /**
+ * The list of BG-FGS-Launch and temp-allow-list reason code.
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "REASON_" }, value = {
+ // BG-FGS-Launch reasons.
+ REASON_DENIED,
+ REASON_UNKNOWN,
+ REASON_OTHER,
+ REASON_PROC_STATE_PERSISTENT,
+ REASON_PROC_STATE_PERSISTENT_UI,
+ REASON_PROC_STATE_TOP,
+ REASON_PROC_STATE_BTOP,
+ REASON_PROC_STATE_FGS,
+ REASON_PROC_STATE_BFGS,
+ REASON_UID_VISIBLE,
+ REASON_SYSTEM_UID,
+ REASON_ACTIVITY_STARTER,
+ REASON_START_ACTIVITY_FLAG,
+ REASON_FGS_BINDING,
+ REASON_DEVICE_OWNER,
+ REASON_PROFILE_OWNER,
+ REASON_COMPANION_DEVICE_MANAGER,
+ REASON_BACKGROUND_ACTIVITY_PERMISSION,
+ REASON_BACKGROUND_FGS_PERMISSION,
+ REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
+ REASON_INSTR_BACKGROUND_FGS_PERMISSION,
+ REASON_SYSTEM_ALERT_WINDOW_PERMISSION,
+ REASON_DEVICE_DEMO_MODE,
+ REASON_EXEMPTED_PACKAGE,
+ REASON_ALLOWLISTED_PACKAGE,
+ REASON_APPOP,
+ REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD,
+ // temp and system allow list reasons.
+ REASON_GEOFENCING,
+ REASON_PUSH_MESSAGING,
+ REASON_PUSH_MESSAGING_OVER_QUOTA,
+ REASON_ACTIVITY_RECOGNITION,
+ REASON_ACCOUNT_TRANSFER,
+ REASON_BOOT_COMPLETED,
+ REASON_PRE_BOOT_COMPLETED,
+ REASON_LOCKED_BOOT_COMPLETED,
+ REASON_SYSTEM_ALLOW_LISTED,
+ REASON_ALARM_MANAGER_ALARM_CLOCK,
+ REASON_ALARM_MANAGER_WHILE_IDLE,
+ REASON_SERVICE_LAUNCH,
+ REASON_KEY_CHAIN,
+ REASON_PACKAGE_VERIFIER,
+ REASON_SYNC_MANAGER,
+ REASON_DOMAIN_VERIFICATION_V1,
+ REASON_DOMAIN_VERIFICATION_V2,
+ REASON_VPN,
+ REASON_NOTIFICATION_SERVICE,
+ REASON_PACKAGE_REPLACED,
+ REASON_LOCATION_PROVIDER,
+ REASON_MEDIA_BUTTON,
+ REASON_EVENT_SMS,
+ REASON_EVENT_MMS,
+ REASON_SHELL,
+ REASON_MEDIA_SESSION_CALLBACK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReasonCode {}
+
+ /**
+ * @hide
+ */
+ public PowerExemptionManager(@NonNull Context context) {
+ mContext = context;
+ mService = context.getSystemService(DeviceIdleManager.class).getService();
+ }
+
+ /**
+ * Add the specified package to the permanent power save allow list.
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void addToPermanentAllowList(@NonNull String packageName) {
+ addToPermanentAllowList(Collections.singletonList(packageName));
+ }
+
+ /**
+ * Add the specified packages to the permanent power save allow list.
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void addToPermanentAllowList(@NonNull List<String> packageNames) {
+ try {
+ mService.addPowerSaveWhitelistApps(packageNames);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get a list of app IDs of app that are allow-listed. This does not include temporarily
+ * allow-listed apps.
+ *
+ * @param includingIdle Set to true if the app should be allow-listed from device idle as well
+ * as other power save restrictions
+ * @hide
+ */
+ @NonNull
+ public int[] getAllowListedAppIds(boolean includingIdle) {
+ try {
+ if (includingIdle) {
+ return mService.getAppIdWhitelist();
+ } else {
+ return mService.getAppIdWhitelistExceptIdle();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns true if the app is allow-listed from power save restrictions. This does not include
+ * temporarily allow-listed apps.
+ *
+ * @param includingIdle Set to true if the app should be allow-listed from device
+ * idle as well as other power save restrictions
+ * @hide
+ */
+ public boolean isAllowListed(@NonNull String packageName, boolean includingIdle) {
+ try {
+ if (includingIdle) {
+ return mService.isPowerSaveWhitelistApp(packageName);
+ } else {
+ return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove an app from the permanent power save allow list. Only apps that were added via
+ * {@link #addToPermanentAllowList(String)} or {@link #addToPermanentAllowList(List)} will be
+ * removed. Apps allow-listed by default by the system cannot be removed.
+ *
+ * @param packageName The app to remove from the allow list
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void removeFromAllowList(@NonNull String packageName) {
+ try {
+ mService.removePowerSaveWhitelistApp(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Add an app to the temporary allow list for a short amount of time.
+ *
+ * @param packageName The package to add to the temp allow list
+ * @param durationMs How long to keep the app on the temp allow list for (in milliseconds)
+ * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+ * @param reason a optional human readable reason string, could be null or empty string.
+ */
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+ public void addToTemporaryAllowList(@NonNull String packageName, long durationMs,
+ @ReasonCode int reasonCode, @Nullable String reason) {
+ try {
+ mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
+ reasonCode, reason);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Add an app to the temporary allow list for a short amount of time for a specific reason.
+ * The temporary allow list is kept separately from the permanent allow list and apps are
+ * automatically removed from the temporary allow list after a predetermined amount of time.
+ *
+ * @param packageName The package to add to the temp allow list
+ * @param event The reason to add the app to the temp allow list
+ * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+ * @param reason A human-readable reason explaining why the app is temp allow-listed. Only
+ * used for logging purposes. Could be null or empty string.
+ * @return The duration (in milliseconds) that the app is allow-listed for
+ */
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+ public long addToTemporaryAllowListForEvent(@NonNull String packageName,
+ @AllowListEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
+ try {
+ switch (event) {
+ case EVENT_MMS:
+ return mService.addPowerSaveTempWhitelistAppForMms(
+ packageName, mContext.getUserId(), reasonCode, reason);
+ case EVENT_SMS:
+ return mService.addPowerSaveTempWhitelistAppForSms(
+ packageName, mContext.getUserId(), reasonCode, reason);
+ case EVENT_UNSPECIFIED:
+ default:
+ return mService.whitelistAppTemporarily(
+ packageName, mContext.getUserId(), reasonCode, reason);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static @ReasonCode int getReasonCodeFromProcState(int procState) {
+ if (procState <= PROCESS_STATE_PERSISTENT) {
+ return REASON_PROC_STATE_PERSISTENT;
+ } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
+ return REASON_PROC_STATE_PERSISTENT_UI;
+ } else if (procState <= PROCESS_STATE_TOP) {
+ return REASON_PROC_STATE_TOP;
+ } else if (procState <= PROCESS_STATE_BOUND_TOP) {
+ return REASON_PROC_STATE_BTOP;
+ } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+ return REASON_PROC_STATE_FGS;
+ } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ return REASON_PROC_STATE_BFGS;
+ } else {
+ return REASON_DENIED;
+ }
+ }
+
+ /**
+ * Return string name of the integer reason code.
+ * @hide
+ * @param reasonCode
+ * @return string name of the reason code.
+ */
+ public static String reasonCodeToString(@ReasonCode int reasonCode) {
+ switch (reasonCode) {
+ case REASON_DENIED:
+ return "DENIED";
+ case REASON_UNKNOWN:
+ return "UNKNOWN";
+ case REASON_OTHER:
+ return "OTHER";
+ case REASON_PROC_STATE_PERSISTENT:
+ return "PROC_STATE_PERSISTENT";
+ case REASON_PROC_STATE_PERSISTENT_UI:
+ return "PROC_STATE_PERSISTENT_UI";
+ case REASON_PROC_STATE_TOP:
+ return "PROC_STATE_TOP";
+ case REASON_PROC_STATE_BTOP:
+ return "PROC_STATE_BTOP";
+ case REASON_PROC_STATE_FGS:
+ return "PROC_STATE_FGS";
+ case REASON_PROC_STATE_BFGS:
+ return "PROC_STATE_BFGS";
+ case REASON_UID_VISIBLE:
+ return "UID_VISIBLE";
+ case REASON_SYSTEM_UID:
+ return "SYSTEM_UID";
+ case REASON_ACTIVITY_STARTER:
+ return "ACTIVITY_STARTER";
+ case REASON_START_ACTIVITY_FLAG:
+ return "START_ACTIVITY_FLAG";
+ case REASON_FGS_BINDING:
+ return "FGS_BINDING";
+ case REASON_DEVICE_OWNER:
+ return "DEVICE_OWNER";
+ case REASON_PROFILE_OWNER:
+ return "PROFILE_OWNER";
+ case REASON_COMPANION_DEVICE_MANAGER:
+ return "COMPANION_DEVICE_MANAGER";
+ case REASON_BACKGROUND_ACTIVITY_PERMISSION:
+ return "BACKGROUND_ACTIVITY_PERMISSION";
+ case REASON_BACKGROUND_FGS_PERMISSION:
+ return "BACKGROUND_FGS_PERMISSION";
+ case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
+ return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
+ case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
+ return "INSTR_BACKGROUND_FGS_PERMISSION";
+ case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
+ return "SYSTEM_ALERT_WINDOW_PERMISSION";
+ case REASON_DEVICE_DEMO_MODE:
+ return "DEVICE_DEMO_MODE";
+ case REASON_EXEMPTED_PACKAGE:
+ return "EXEMPTED_PACKAGE";
+ case REASON_ALLOWLISTED_PACKAGE:
+ return "ALLOWLISTED_PACKAGE";
+ case REASON_APPOP:
+ return "APPOP";
+ case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD:
+ return "ACTIVITY_VISIBILITY_GRACE_PERIOD";
+ case REASON_GEOFENCING:
+ return "GEOFENCING";
+ case REASON_PUSH_MESSAGING:
+ return "PUSH_MESSAGING";
+ case REASON_PUSH_MESSAGING_OVER_QUOTA:
+ return "PUSH_MESSAGING_OVER_QUOTA";
+ case REASON_ACTIVITY_RECOGNITION:
+ return "ACTIVITY_RECOGNITION";
+ case REASON_ACCOUNT_TRANSFER:
+ return "REASON_ACCOUNT_TRANSFER";
+ case REASON_BOOT_COMPLETED:
+ return "BOOT_COMPLETED";
+ case REASON_PRE_BOOT_COMPLETED:
+ return "PRE_BOOT_COMPLETED";
+ case REASON_LOCKED_BOOT_COMPLETED:
+ return "LOCKED_BOOT_COMPLETED";
+ case REASON_SYSTEM_ALLOW_LISTED:
+ return "SYSTEM_ALLOW_LISTED";
+ case REASON_ALARM_MANAGER_ALARM_CLOCK:
+ return "ALARM_MANAGER_ALARM_CLOCK";
+ case REASON_ALARM_MANAGER_WHILE_IDLE:
+ return "ALARM_MANAGER_WHILE_IDLE";
+ case REASON_SERVICE_LAUNCH:
+ return "SERVICE_LAUNCH";
+ case REASON_KEY_CHAIN:
+ return "KEY_CHAIN";
+ case REASON_PACKAGE_VERIFIER:
+ return "PACKAGE_VERIFIER";
+ case REASON_SYNC_MANAGER:
+ return "SYNC_MANAGER";
+ case REASON_DOMAIN_VERIFICATION_V1:
+ return "DOMAIN_VERIFICATION_V1";
+ case REASON_DOMAIN_VERIFICATION_V2:
+ return "DOMAIN_VERIFICATION_V2";
+ case REASON_VPN:
+ return "VPN";
+ case REASON_NOTIFICATION_SERVICE:
+ return "NOTIFICATION_SERVICE";
+ case REASON_PACKAGE_REPLACED:
+ return "PACKAGE_REPLACED";
+ case REASON_LOCATION_PROVIDER:
+ return "LOCATION_PROVIDER";
+ case REASON_MEDIA_BUTTON:
+ return "MEDIA_BUTTON";
+ case REASON_EVENT_SMS:
+ return "EVENT_SMS";
+ case REASON_EVENT_MMS:
+ return "EVENT_MMS";
+ case REASON_SHELL:
+ return "SHELL";
+ case REASON_MEDIA_SESSION_CALLBACK:
+ return "MEDIA_SESSION_CALLBACK";
+ default:
+ return "(unknown:" + reasonCode + ")";
+ }
+ }
+}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index b1b733a599c6..eba39c7573be 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -16,13 +16,6 @@
package android.os;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
-import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-import static android.app.ActivityManager.PROCESS_STATE_TOP;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,7 +26,6 @@ import android.content.Context;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
import java.util.List;
/**
@@ -43,9 +35,11 @@ import java.util.List;
* placed on the temporary whitelist are removed from that whitelist after a predetermined amount of
* time.
*
+ * @deprecated Use {@link PowerExemptionManager} instead
* @hide
*/
@SystemApi
+@Deprecated
@SystemService(Context.POWER_WHITELIST_MANAGER)
public class PowerWhitelistManager {
private final Context mContext;
@@ -53,21 +47,23 @@ public class PowerWhitelistManager {
// TODO: migrate to PowerWhitelistController
private final IDeviceIdleController mService;
+ private final PowerExemptionManager mPowerExemptionManager;
+
/**
* Indicates that an unforeseen event has occurred and the app should be whitelisted to handle
* it.
*/
- public static final int EVENT_UNSPECIFIED = 0;
+ public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED;
/**
* Indicates that an SMS event has occurred and the app should be whitelisted to handle it.
*/
- public static final int EVENT_SMS = 1;
+ public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS;
/**
* Indicates that an MMS event has occurred and the app should be whitelisted to handle it.
*/
- public static final int EVENT_MMS = 2;
+ public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS;
/**
* @hide
@@ -84,12 +80,14 @@ public class PowerWhitelistManager {
/**
* Allow the temp allowlist behavior, plus allow foreground service start from background.
*/
- public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
/**
* Only allow the temp allowlist behavior, not allow foreground service start from
* background.
*/
- public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
/**
* The list of temp allowlist types.
@@ -107,73 +105,83 @@ public class PowerWhitelistManager {
* BG-FGS-launch is denied.
* @hide
*/
- public static final int REASON_DENIED = -1;
+ public static final int REASON_DENIED = PowerExemptionManager.REASON_DENIED;
/* Reason code range 0-9 are reserved for default reasons */
/**
* The default reason code if reason is unknown.
*/
- public static final int REASON_UNKNOWN = 0;
+ public static final int REASON_UNKNOWN = PowerExemptionManager.REASON_UNKNOWN;
/**
* Use REASON_OTHER if there is no better choice.
*/
- public static final int REASON_OTHER = 1;
+ public static final int REASON_OTHER = PowerExemptionManager.REASON_OTHER;
/* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
/** @hide */
- public static final int REASON_PROC_STATE_PERSISTENT = 10;
+ public static final int REASON_PROC_STATE_PERSISTENT =
+ PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
/** @hide */
- public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+ public static final int REASON_PROC_STATE_PERSISTENT_UI =
+ PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
/** @hide */
- public static final int REASON_PROC_STATE_TOP = 12;
+ public static final int REASON_PROC_STATE_TOP = PowerExemptionManager.REASON_PROC_STATE_TOP;
/** @hide */
- public static final int REASON_PROC_STATE_BTOP = 13;
+ public static final int REASON_PROC_STATE_BTOP = PowerExemptionManager.REASON_PROC_STATE_BTOP;
/** @hide */
- public static final int REASON_PROC_STATE_FGS = 14;
+ public static final int REASON_PROC_STATE_FGS = PowerExemptionManager.REASON_PROC_STATE_FGS;
/** @hide */
- public static final int REASON_PROC_STATE_BFGS = 15;
+ public static final int REASON_PROC_STATE_BFGS = PowerExemptionManager.REASON_PROC_STATE_BFGS;
/* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
/** @hide */
- public static final int REASON_UID_VISIBLE = 50;
+ public static final int REASON_UID_VISIBLE = PowerExemptionManager.REASON_UID_VISIBLE;
/** @hide */
- public static final int REASON_SYSTEM_UID = 51;
+ public static final int REASON_SYSTEM_UID = PowerExemptionManager.REASON_SYSTEM_UID;
/** @hide */
- public static final int REASON_ACTIVITY_STARTER = 52;
+ public static final int REASON_ACTIVITY_STARTER = PowerExemptionManager.REASON_ACTIVITY_STARTER;
/** @hide */
- public static final int REASON_START_ACTIVITY_FLAG = 53;
+ public static final int REASON_START_ACTIVITY_FLAG =
+ PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
/** @hide */
- public static final int REASON_FGS_BINDING = 54;
+ public static final int REASON_FGS_BINDING = PowerExemptionManager.REASON_FGS_BINDING;
/** @hide */
- public static final int REASON_DEVICE_OWNER = 55;
+ public static final int REASON_DEVICE_OWNER = PowerExemptionManager.REASON_DEVICE_OWNER;
/** @hide */
- public static final int REASON_PROFILE_OWNER = 56;
+ public static final int REASON_PROFILE_OWNER = PowerExemptionManager.REASON_PROFILE_OWNER;
/** @hide */
- public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+ public static final int REASON_COMPANION_DEVICE_MANAGER =
+ PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
/**
* START_ACTIVITIES_FROM_BACKGROUND permission.
* @hide
*/
- public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+ public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION =
+ PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
/**
* START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
* @hide
*/
- public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+ public static final int REASON_BACKGROUND_FGS_PERMISSION =
+ PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION;
/** @hide */
- public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+ public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION =
+ PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
/** @hide */
- public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+ public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION =
+ PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
/** @hide */
- public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+ public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION =
+ PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
/** @hide */
- public static final int REASON_DEVICE_DEMO_MODE = 63;
+ public static final int REASON_DEVICE_DEMO_MODE = PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
/** @hide */
- public static final int REASON_EXEMPTED_PACKAGE = 64;
+ public static final int REASON_EXEMPTED_PACKAGE = PowerExemptionManager.REASON_EXEMPTED_PACKAGE;
/** @hide */
- public static final int REASON_ALLOWLISTED_PACKAGE = 65;
+ public static final int REASON_ALLOWLISTED_PACKAGE =
+ PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
/** @hide */
- public static final int REASON_APPOP = 66;
+ public static final int REASON_APPOP = PowerExemptionManager.REASON_APPOP;
/* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
Reason code for temp and system allowlist starts here.
@@ -181,117 +189,128 @@ public class PowerWhitelistManager {
/**
* Set temp-allowlist for location geofence purpose.
*/
- public static final int REASON_GEOFENCING = 100;
+ public static final int REASON_GEOFENCING = PowerExemptionManager.REASON_GEOFENCING;
/**
* Set temp-allowlist for server push messaging.
*/
- public static final int REASON_PUSH_MESSAGING = 101;
+ public static final int REASON_PUSH_MESSAGING = PowerExemptionManager.REASON_PUSH_MESSAGING;
/**
* Set temp-allowlist for server push messaging over the quota.
*/
- public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+ public static final int REASON_PUSH_MESSAGING_OVER_QUOTA =
+ PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA;
/**
* Set temp-allowlist for activity recognition.
*/
- public static final int REASON_ACTIVITY_RECOGNITION = 103;
+ public static final int REASON_ACTIVITY_RECOGNITION =
+ PowerExemptionManager.REASON_ACTIVITY_RECOGNITION;
/* Reason code range 200-299 are reserved for broadcast actions */
/**
* Broadcast ACTION_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_BOOT_COMPLETED = 200;
+ public static final int REASON_BOOT_COMPLETED = PowerExemptionManager.REASON_BOOT_COMPLETED;
/**
* Broadcast ACTION_PRE_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_PRE_BOOT_COMPLETED = 201;
+ public static final int REASON_PRE_BOOT_COMPLETED =
+ PowerExemptionManager.REASON_PRE_BOOT_COMPLETED;
/**
* Broadcast ACTION_LOCKED_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+ public static final int REASON_LOCKED_BOOT_COMPLETED =
+ PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
/* Reason code range 300-399 are reserved for other internal reasons */
/**
* Device idle system allowlist, including EXCEPT-IDLE
* @hide
*/
- public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
+ public static final int REASON_SYSTEM_ALLOW_LISTED =
+ PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
/** @hide */
- public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+ public static final int REASON_ALARM_MANAGER_ALARM_CLOCK =
+ PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
/**
* AlarmManagerService.
* @hide
*/
- public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+ public static final int REASON_ALARM_MANAGER_WHILE_IDLE =
+ PowerExemptionManager.REASON_ALARM_MANAGER_WHILE_IDLE;
/**
* ActiveServices.
* @hide
*/
- public static final int REASON_SERVICE_LAUNCH = 303;
+ public static final int REASON_SERVICE_LAUNCH = PowerExemptionManager.REASON_SERVICE_LAUNCH;
/**
* KeyChainSystemService.
* @hide
*/
- public static final int REASON_KEY_CHAIN = 304;
+ public static final int REASON_KEY_CHAIN = PowerExemptionManager.REASON_KEY_CHAIN;
/**
* PackageManagerService.
* @hide
*/
- public static final int REASON_PACKAGE_VERIFIER = 305;
+ public static final int REASON_PACKAGE_VERIFIER = PowerExemptionManager.REASON_PACKAGE_VERIFIER;
/**
* SyncManager.
* @hide
*/
- public static final int REASON_SYNC_MANAGER = 306;
+ public static final int REASON_SYNC_MANAGER = PowerExemptionManager.REASON_SYNC_MANAGER;
/**
* DomainVerificationProxyV1.
* @hide
*/
- public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+ public static final int REASON_DOMAIN_VERIFICATION_V1 =
+ PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V1;
/**
* DomainVerificationProxyV2.
* @hide
*/
- public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+ public static final int REASON_DOMAIN_VERIFICATION_V2 =
+ PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V2;
/** @hide */
public static final int REASON_VPN = 309;
/**
* NotificationManagerService.
* @hide
*/
- public static final int REASON_NOTIFICATION_SERVICE = 310;
+ public static final int REASON_NOTIFICATION_SERVICE =
+ PowerExemptionManager.REASON_NOTIFICATION_SERVICE;
/**
* Broadcast ACTION_MY_PACKAGE_REPLACED.
* @hide
*/
- public static final int REASON_PACKAGE_REPLACED = 311;
+ public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED;
/**
* LocationProviderManager.
* @hide
*/
- public static final int REASON_LOCATION_PROVIDER = 312;
+ public static final int REASON_LOCATION_PROVIDER =
+ PowerExemptionManager.REASON_LOCATION_PROVIDER;
/**
* MediaButtonReceiver.
* @hide
*/
- public static final int REASON_MEDIA_BUTTON = 313;
+ public static final int REASON_MEDIA_BUTTON = PowerExemptionManager.REASON_MEDIA_BUTTON;
/**
* InboundSmsHandler.
* @hide
*/
- public static final int REASON_EVENT_SMS = 314;
+ public static final int REASON_EVENT_SMS = PowerExemptionManager.REASON_EVENT_SMS;
/**
* InboundSmsHandler.
* @hide
*/
- public static final int REASON_EVENT_MMS = 315;
+ public static final int REASON_EVENT_MMS = PowerExemptionManager.REASON_EVENT_MMS;
/**
* Shell app.
* @hide
*/
- public static final int REASON_SHELL = 316;
+ public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL;
/**
* The list of BG-FGS-Launch and temp-allowlist reason code.
@@ -360,26 +379,29 @@ public class PowerWhitelistManager {
public PowerWhitelistManager(@NonNull Context context) {
mContext = context;
mService = context.getSystemService(DeviceIdleManager.class).getService();
+ mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
}
/**
* Add the specified package to the permanent power save whitelist.
+ *
+ * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void addToWhitelist(@NonNull String packageName) {
- addToWhitelist(Collections.singletonList(packageName));
+ mPowerExemptionManager.addToPermanentAllowList(packageName);
}
/**
* Add the specified packages to the permanent power save whitelist.
+ *
+ * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void addToWhitelist(@NonNull List<String> packageNames) {
- try {
- mService.addPowerSaveWhitelistApps(packageNames);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPowerExemptionManager.addToPermanentAllowList(packageNames);
}
/**
@@ -388,19 +410,13 @@ public class PowerWhitelistManager {
*
* @param includingIdle Set to true if the app should be whitelisted from device idle as well
* as other power save restrictions
+ * @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead
* @hide
*/
+ @Deprecated
@NonNull
public int[] getWhitelistedAppIds(boolean includingIdle) {
- try {
- if (includingIdle) {
- return mService.getAppIdWhitelist();
- } else {
- return mService.getAppIdWhitelistExceptIdle();
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mPowerExemptionManager.getAllowListedAppIds(includingIdle);
}
/**
@@ -409,18 +425,12 @@ public class PowerWhitelistManager {
*
* @param includingIdle Set to true if the app should be whitelisted from device
* idle as well as other power save restrictions
+ * @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead
* @hide
*/
+ @Deprecated
public boolean isWhitelisted(@NonNull String packageName, boolean includingIdle) {
- try {
- if (includingIdle) {
- return mService.isPowerSaveWhitelistApp(packageName);
- } else {
- return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mPowerExemptionManager.isAllowListed(packageName, includingIdle);
}
/**
@@ -429,14 +439,12 @@ public class PowerWhitelistManager {
* whitelisted by default by the system cannot be removed.
*
* @param packageName The app to remove from the whitelist
+ * @deprecated Use {@link PowerExemptionManager#removeFromAllowList(String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void removeFromWhitelist(@NonNull String packageName) {
- try {
- mService.removePowerSaveWhitelistApp(packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPowerExemptionManager.removeFromAllowList(packageName);
}
/**
@@ -446,16 +454,14 @@ public class PowerWhitelistManager {
* @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
* @param reason a optional human readable reason string, could be null or empty string.
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+ * String, long, int, String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
@ReasonCode int reasonCode, @Nullable String reason) {
- try {
- mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
- reasonCode, reason);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPowerExemptionManager.addToTemporaryAllowList(packageName, durationMs, reasonCode, reason);
}
/**
@@ -463,12 +469,14 @@ public class PowerWhitelistManager {
*
* @param packageName The package to add to the temp whitelist
* @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
- * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+ * String, long, int, String)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
- whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName);
+ mPowerExemptionManager.addToTemporaryAllowList(
+ packageName, durationMs, REASON_UNKNOWN, packageName);
}
/**
@@ -481,13 +489,15 @@ public class PowerWhitelistManager {
* @param reason A human-readable reason explaining why the app is temp whitelisted. Only
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is whitelisted for
- * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+ * String, int, int, String)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
@WhitelistEvent int event, @Nullable String reason) {
- return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason);
+ return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+ packageName, event, REASON_UNKNOWN, reason);
}
/**
@@ -501,47 +511,25 @@ public class PowerWhitelistManager {
* @param reason A human-readable reason explaining why the app is temp whitelisted. Only
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is whitelisted for
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+ * String, int, int, String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
@WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
- try {
- switch (event) {
- case EVENT_MMS:
- return mService.addPowerSaveTempWhitelistAppForMms(
- packageName, mContext.getUserId(), reasonCode, reason);
- case EVENT_SMS:
- return mService.addPowerSaveTempWhitelistAppForSms(
- packageName, mContext.getUserId(), reasonCode, reason);
- case EVENT_UNSPECIFIED:
- default:
- return mService.whitelistAppTemporarily(
- packageName, mContext.getUserId(), reasonCode, reason);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+ packageName, event, reasonCode, reason);
}
/**
* @hide
+ *
+ * @deprecated Use {@link PowerExemptionManager#getReasonCodeFromProcState(int)} instead
*/
+ @Deprecated
public static @ReasonCode int getReasonCodeFromProcState(int procState) {
- if (procState <= PROCESS_STATE_PERSISTENT) {
- return REASON_PROC_STATE_PERSISTENT;
- } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
- return REASON_PROC_STATE_PERSISTENT_UI;
- } else if (procState <= PROCESS_STATE_TOP) {
- return REASON_PROC_STATE_TOP;
- } else if (procState <= PROCESS_STATE_BOUND_TOP) {
- return REASON_PROC_STATE_BTOP;
- } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
- return REASON_PROC_STATE_FGS;
- } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- return REASON_PROC_STATE_BFGS;
- } else {
- return REASON_DENIED;
- }
+ return PowerExemptionManager.getReasonCodeFromProcState(procState);
}
/**
@@ -549,111 +537,10 @@ public class PowerWhitelistManager {
* @hide
* @param reasonCode
* @return string name of the reason code.
+ * @deprecated Use {@link PowerExemptionManager#reasonCodeToString(int)} instead
*/
+ @Deprecated
public static String reasonCodeToString(@ReasonCode int reasonCode) {
- switch (reasonCode) {
- case REASON_DENIED:
- return "DENIED";
- case REASON_UNKNOWN:
- return "UNKNOWN";
- case REASON_OTHER:
- return "OTHER";
- case REASON_PROC_STATE_PERSISTENT:
- return "PROC_STATE_PERSISTENT";
- case REASON_PROC_STATE_PERSISTENT_UI:
- return "PROC_STATE_PERSISTENT_UI";
- case REASON_PROC_STATE_TOP:
- return "PROC_STATE_TOP";
- case REASON_PROC_STATE_BTOP:
- return "PROC_STATE_BTOP";
- case REASON_PROC_STATE_FGS:
- return "PROC_STATE_FGS";
- case REASON_PROC_STATE_BFGS:
- return "PROC_STATE_BFGS";
- case REASON_UID_VISIBLE:
- return "UID_VISIBLE";
- case REASON_SYSTEM_UID:
- return "SYSTEM_UID";
- case REASON_ACTIVITY_STARTER:
- return "ACTIVITY_STARTER";
- case REASON_START_ACTIVITY_FLAG:
- return "START_ACTIVITY_FLAG";
- case REASON_FGS_BINDING:
- return "FGS_BINDING";
- case REASON_DEVICE_OWNER:
- return "DEVICE_OWNER";
- case REASON_PROFILE_OWNER:
- return "PROFILE_OWNER";
- case REASON_COMPANION_DEVICE_MANAGER:
- return "COMPANION_DEVICE_MANAGER";
- case REASON_BACKGROUND_ACTIVITY_PERMISSION:
- return "BACKGROUND_ACTIVITY_PERMISSION";
- case REASON_BACKGROUND_FGS_PERMISSION:
- return "BACKGROUND_FGS_PERMISSION";
- case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
- return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
- case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
- return "INSTR_BACKGROUND_FGS_PERMISSION";
- case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
- return "SYSTEM_ALERT_WINDOW_PERMISSION";
- case REASON_DEVICE_DEMO_MODE:
- return "DEVICE_DEMO_MODE";
- case REASON_EXEMPTED_PACKAGE:
- return "EXEMPTED_PACKAGE";
- case REASON_ALLOWLISTED_PACKAGE:
- return "ALLOWLISTED_PACKAGE";
- case REASON_APPOP:
- return "APPOP";
- case REASON_GEOFENCING:
- return "GEOFENCING";
- case REASON_PUSH_MESSAGING:
- return "PUSH_MESSAGING";
- case REASON_PUSH_MESSAGING_OVER_QUOTA:
- return "PUSH_MESSAGING_OVER_QUOTA";
- case REASON_ACTIVITY_RECOGNITION:
- return "ACTIVITY_RECOGNITION";
- case REASON_BOOT_COMPLETED:
- return "BOOT_COMPLETED";
- case REASON_PRE_BOOT_COMPLETED:
- return "PRE_BOOT_COMPLETED";
- case REASON_LOCKED_BOOT_COMPLETED:
- return "LOCKED_BOOT_COMPLETED";
- case REASON_SYSTEM_ALLOW_LISTED:
- return "SYSTEM_ALLOW_LISTED";
- case REASON_ALARM_MANAGER_ALARM_CLOCK:
- return "ALARM_MANAGER_ALARM_CLOCK";
- case REASON_ALARM_MANAGER_WHILE_IDLE:
- return "ALARM_MANAGER_WHILE_IDLE";
- case REASON_SERVICE_LAUNCH:
- return "SERVICE_LAUNCH";
- case REASON_KEY_CHAIN:
- return "KEY_CHAIN";
- case REASON_PACKAGE_VERIFIER:
- return "PACKAGE_VERIFIER";
- case REASON_SYNC_MANAGER:
- return "SYNC_MANAGER";
- case REASON_DOMAIN_VERIFICATION_V1:
- return "DOMAIN_VERIFICATION_V1";
- case REASON_DOMAIN_VERIFICATION_V2:
- return "DOMAIN_VERIFICATION_V2";
- case REASON_VPN:
- return "VPN";
- case REASON_NOTIFICATION_SERVICE:
- return "NOTIFICATION_SERVICE";
- case REASON_PACKAGE_REPLACED:
- return "PACKAGE_REPLACED";
- case REASON_LOCATION_PROVIDER:
- return "LOCATION_PROVIDER";
- case REASON_MEDIA_BUTTON:
- return "MEDIA_BUTTON";
- case REASON_EVENT_SMS:
- return "EVENT_SMS";
- case REASON_EVENT_MMS:
- return "EVENT_MMS";
- case REASON_SHELL:
- return "SHELL";
- default:
- return "(unknown:" + reasonCode + ")";
- }
+ return PowerExemptionManager.reasonCodeToString(reasonCode);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 119dcb63770d..667fc60fb20b 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2692,7 +2692,7 @@ public class DeviceIdleController extends SystemService
void addPowerSaveTempAllowlistAppChecked(String packageName, long duration,
int userId, @ReasonCode int reasonCode, @Nullable String reason)
throws RemoteException {
- getContext().enforceCallingPermission(
+ getContext().enforceCallingOrSelfPermission(
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
"No permission to change device idle whitelist");
final int callingUid = Binder.getCallingUid();
@@ -2715,7 +2715,7 @@ public class DeviceIdleController extends SystemService
void removePowerSaveTempAllowlistAppChecked(String packageName, int userId)
throws RemoteException {
- getContext().enforceCallingPermission(
+ getContext().enforceCallingOrSelfPermission(
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
"No permission to change device idle whitelist");
final int callingUid = Binder.getCallingUid();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 33f6e0651abc..58fc87476f2a 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -388,6 +388,8 @@ public class AlarmManagerService extends SystemService {
@VisibleForTesting
static final String KEY_MAX_INTERVAL = "max_interval";
@VisibleForTesting
+ static final String KEY_MIN_WINDOW = "min_window";
+ @VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
= "allow_while_idle_whitelist_duration";
@VisibleForTesting
@@ -428,11 +430,13 @@ public class AlarmManagerService extends SystemService {
@VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW = "allow_while_idle_compat_window";
- private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
+ @VisibleForTesting
+ static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
+ private static final long DEFAULT_MIN_WINDOW = 10_000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -475,6 +479,9 @@ public class AlarmManagerService extends SystemService {
// Maximum alarm recurrence interval
public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;
+ // Minimum window size for inexact alarms
+ public long MIN_WINDOW = DEFAULT_MIN_WINDOW;
+
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
= DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -575,6 +582,9 @@ public class AlarmManagerService extends SystemService {
ALLOW_WHILE_IDLE_QUOTA = 1;
}
break;
+ case KEY_MIN_WINDOW:
+ MIN_WINDOW = properties.getLong(KEY_MIN_WINDOW, DEFAULT_MIN_WINDOW);
+ break;
case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA:
ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt(
KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA,
@@ -738,6 +748,11 @@ public class AlarmManagerService extends SystemService {
TimeUtils.formatDuration(MAX_INTERVAL, pw);
pw.println();
+ pw.print(KEY_MIN_WINDOW);
+ pw.print("=");
+ TimeUtils.formatDuration(MIN_WINDOW, pw);
+ pw.println();
+
pw.print(KEY_LISTENER_TIMEOUT);
pw.print("=");
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
@@ -1642,6 +1657,7 @@ public class AlarmManagerService extends SystemService {
// Fix this window in place, so that as time approaches we don't collapse it.
windowLength = maxElapsed - triggerElapsed;
} else {
+ windowLength = Math.max(windowLength, mConstants.MIN_WINDOW);
maxElapsed = triggerElapsed + windowLength;
}
synchronized (mLock) {
@@ -1981,8 +1997,10 @@ public class AlarmManagerService extends SystemService {
* Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
*/
- boolean isExemptFromPermission(int uid) {
- return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null
+ boolean isExemptFromExactAlarmPermission(int uid) {
+ return (UserHandle.isSameApp(mSystemUiUid, uid)
+ || UserHandle.isCore(uid)
+ || mLocalDeviceIdleController == null
|| mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid)));
}
@@ -2002,54 +2020,43 @@ public class AlarmManagerService extends SystemService {
mAppOps.checkPackage(callingUid, callingPackage);
final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
+ final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT);
+ // make sure the caller is allowed to use the requested kind of alarm, and also
+ // decide what quota and broadcast options to use.
Bundle idleOptions = null;
- if (alarmClock != null || allowWhileIdle) {
- // make sure the caller is allowed to use the requested kind of alarm, and also
- // decide what broadcast options to use.
+ if (exact || allowWhileIdle) {
final boolean needsPermission;
- boolean lowQuota;
+ boolean lowerQuota;
if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
- if (windowLength != AlarmManager.WINDOW_EXACT) {
- needsPermission = false;
- lowQuota = true;
- idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle()
- : mOptsWithoutFgs.toBundle();
- } else if (alarmClock != null) {
- needsPermission = true;
- lowQuota = false;
- idleOptions = mOptsWithFgs.toBundle();
- } else {
- needsPermission = true;
- lowQuota = false;
- idleOptions = mOptsWithFgs.toBundle();
- }
+ needsPermission = exact;
+ lowerQuota = !exact;
+ idleOptions = exact ? mOptsWithFgs.toBundle() : mOptsWithoutFgs.toBundle();
} else {
needsPermission = false;
- lowQuota = allowWhileIdle;
+ lowerQuota = allowWhileIdle;
idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
}
if (needsPermission && !canScheduleExactAlarms()) {
- if (alarmClock == null && isExemptFromPermission(callingUid)) {
- // If the app is on the full system allow-list (not except-idle), we still
- // allow the alarms, but with a lower quota to keep pre-S compatibility.
- lowQuota = true;
- } else {
+ if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) {
final String errorMessage = "Caller " + callingPackage + " needs to hold "
+ Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
- + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
- + " alarms.";
+ + "exact alarms.";
if (mConstants.CRASH_NON_CLOCK_APPS) {
throw new SecurityException(errorMessage);
} else {
Slog.wtf(TAG, errorMessage);
- idleOptions = mOptsWithoutFgs.toBundle();
- lowQuota = allowWhileIdle;
}
}
+ // If the app is on the full system power allow-list (not except-idle), or we're
+ // in a soft failure mode, we still allow the alarms.
+ // We give temporary allowlist to allow-while-idle alarms but without FGS
+ // capability. Note that apps that are in the power allow-list do not need it.
+ idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null;
+ lowerQuota = allowWhileIdle;
}
- if (lowQuota) {
+ if (lowerQuota) {
flags &= ~FLAG_ALLOW_WHILE_IDLE;
flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
}
@@ -2998,13 +3005,10 @@ public class AlarmManagerService extends SystemService {
/**
* Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms
* that the app is no longer eligible to use.
- * TODO (b/179541791): Revisit and write tests once UX is final.
+ * TODO (b/179541791): Add revocation history to dumpsys.
*/
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
- if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
- return;
- }
- if (isExemptFromPermission(uid)) {
+ if (isExemptFromExactAlarmPermission(uid)) {
return;
}
if (!CompatChanges.isChangeEnabled(
@@ -3015,7 +3019,7 @@ public class AlarmManagerService extends SystemService {
final Predicate<Alarm> whichAlarms =
a -> (a.uid == uid && a.packageName.equals(packageName)
- && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null));
+ && a.windowLength == AlarmManager.WINDOW_EXACT);
final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
final boolean didRemove = !removed.isEmpty();
if (didRemove) {
@@ -3873,6 +3877,7 @@ public class AlarmManagerService extends SystemService {
return alarm.creatorUid;
}
+
@VisibleForTesting
class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
index 0e442d09d5a5..e684b84748b1 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
@@ -134,6 +134,11 @@ public interface AlarmStore {
void dumpProto(ProtoOutputStream pos, long nowElapsed);
/**
+ * @return a name for this alarm store that can be used for debugging and tests.
+ */
+ String getName();
+
+ /**
* A functional interface used to update the alarm. Used to describe the update in
* {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
*/
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
index e7edfb7b56b9..cb528ba2769a 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StatLogger;
import java.text.SimpleDateFormat;
@@ -40,6 +41,8 @@ import java.util.function.Predicate;
* This keeps the alarms in batches, which are sorted on the start time of their delivery window.
*/
public class BatchingAlarmStore implements AlarmStore {
+ @VisibleForTesting
+ static final String TAG = BatchingAlarmStore.class.getSimpleName();
private final ArrayList<Batch> mAlarmBatches = new ArrayList<>();
private int mSize;
@@ -49,7 +52,7 @@ public class BatchingAlarmStore implements AlarmStore {
int REBATCH_ALL_ALARMS = 0;
}
- final StatLogger mStatLogger = new StatLogger("BatchingAlarmStore stats", new String[]{
+ final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
"REBATCH_ALL_ALARMS",
});
@@ -211,6 +214,11 @@ public class BatchingAlarmStore implements AlarmStore {
}
}
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
private void insertAndBatchAlarm(Alarm alarm) {
final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
: attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index 8ca14463a3b5..c37d2c36b068 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -25,6 +25,7 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StatLogger;
import java.text.SimpleDateFormat;
@@ -38,6 +39,8 @@ import java.util.function.Predicate;
* This keeps the alarms in a sorted list, and only batches them at the time of delivery.
*/
public class LazyAlarmStore implements AlarmStore {
+ @VisibleForTesting
+ static final String TAG = LazyAlarmStore.class.getSimpleName();
private final ArrayList<Alarm> mAlarms = new ArrayList<>();
private Runnable mOnAlarmClockRemoved;
@@ -47,7 +50,7 @@ public class LazyAlarmStore implements AlarmStore {
int GET_NEXT_WAKEUP_DELIVERY_TIME = 1;
}
- final StatLogger mStatLogger = new StatLogger("LazyAlarmStore stats", new String[]{
+ final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{
"GET_NEXT_DELIVERY_TIME",
"GET_NEXT_WAKEUP_DELIVERY_TIME",
});
@@ -214,4 +217,9 @@ public class LazyAlarmStore implements AlarmStore {
a.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARMS, nowElapsed);
}
}
+
+ @Override
+ public String getName() {
+ return TAG;
+ }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 515cb747a99e..82f2f69bbde5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -147,10 +147,8 @@ public class JobSchedulerService extends com.android.server.SystemService
/** The maximum number of concurrent jobs we run at one time. */
static final int MAX_JOB_CONTEXTS_COUNT = 16;
- /** Enforce a per-app limit on scheduled jobs? */
- private static final boolean ENFORCE_MAX_JOBS = true;
- /** The maximum number of jobs that we allow an unprivileged app to schedule */
- private static final int MAX_JOBS_PER_APP = 100;
+ /** The maximum number of jobs that we allow an app to schedule */
+ private static final int MAX_JOBS_PER_APP = 150;
/** The number of the most recently completed jobs to keep track of for debugging purposes. */
private static final int NUM_COMPLETED_JOB_HISTORY = 20;
@@ -1011,7 +1009,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
// Jobs on behalf of others don't apply to the per-app job cap
- if (ENFORCE_MAX_JOBS && packageName == null) {
+ if (packageName == null) {
if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
Slog.w(TAG, "Too many jobs for uid " + uId);
throw new IllegalStateException("Apps may not schedule more than "
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index b70e68b739d8..2f3ac225a190 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -813,12 +813,21 @@ public final class QuotaController extends StateController {
// 1. it's already running (already executing expedited jobs should be allowed to finish)
// 2. the app is currently in the foreground
// 3. the app overall is within its quota
+ // 4. It's on the temp allowlist (or within the grace period)
if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
return true;
}
+ final long tempAllowlistGracePeriodEndElapsed =
+ mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
+ final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
+ || sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed;
+ if (hasTempAllowlistExemption) {
+ return true;
+ }
+
Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
- // Any already executing expedited jbos should be allowed to finish.
+ // Any already executing expedited jobs should be allowed to finish.
if (ejTimer != null && ejTimer.isRunning(jobStatus)) {
return true;
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 3d129d8548eb..20ce13322fd2 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -135,6 +135,10 @@ java_sdk_library {
":updatable-media-srcs",
],
+ api_lint: {
+ enabled: false,
+ },
+
libs: [
"framework_media_annotation",
],
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index af2d2f75b823..1d912ebc71fa 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -3,38 +3,19 @@ package android.media {
public final class MediaTranscodeManager {
method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener);
- field public static final int PRIORITY_REALTIME = 1; // 0x1
- field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession);
}
- public static final class MediaTranscodeManager.TranscodingRequest {
+ public abstract static class MediaTranscodeManager.TranscodingRequest {
method public int getClientPid();
method public int getClientUid();
method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor();
method @NonNull public android.net.Uri getDestinationUri();
- method public int getPriority();
method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor();
method @NonNull public android.net.Uri getSourceUri();
- method public int getType();
- method @Nullable public android.media.MediaFormat getVideoTrackFormat();
- }
-
- public static final class MediaTranscodeManager.TranscodingRequest.Builder {
- ctor public MediaTranscodeManager.TranscodingRequest.Builder();
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
- method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
}
public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
@@ -47,12 +28,16 @@ package android.media {
public static final class MediaTranscodeManager.TranscodingSession {
method public void cancel();
+ method public int getErrorCode();
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getSessionId();
method public int getStatus();
method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+ field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1
+ field public static final int ERROR_NONE = 0; // 0x0
+ field public static final int ERROR_SERVICE_DIED = 2; // 0x2
field public static final int RESULT_CANCELED = 4; // 0x4
field public static final int RESULT_ERROR = 3; // 0x3
field public static final int RESULT_NONE = 1; // 0x1
@@ -67,5 +52,18 @@ package android.media {
method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int);
}
+ public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest {
+ method @NonNull public android.media.MediaFormat getVideoTrackFormat();
+ }
+
+ public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder {
+ ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
+ method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int);
+ method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(android.os.ParcelFileDescriptor);
+ method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(android.os.ParcelFileDescriptor);
+ }
+
}
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 906071fe4c7b..3f30d3ea880e 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -19,11 +19,14 @@ package android.media;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import com.android.modules.annotation.MinSdk;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -77,6 +80,7 @@ import java.util.Set;
There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
application will only need to specify individual types they supported.
*/
+@MinSdk(Build.VERSION_CODES.S)
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java
index 0e461888a0e3..8d1b159cd70b 100644
--- a/apex/media/framework/java/android/media/MediaFeature.java
+++ b/apex/media/framework/java/android/media/MediaFeature.java
@@ -17,6 +17,9 @@
package android.media;
import android.annotation.StringDef;
+import android.os.Build;
+
+import com.android.modules.annotation.MinSdk;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,6 +27,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* MediaFeature defines various media features, e.g. hdr type.
*/
+@MinSdk(Build.VERSION_CODES.S)
public final class MediaFeature {
/**
* Defines tye type of HDR(high dynamic range) video.
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index 93328355026e..de2924e160b6 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -21,7 +21,9 @@ import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
import android.app.SystemServiceRegistry;
import android.content.Context;
+import android.os.Build;
+import com.android.modules.annotation.MinSdk;
import com.android.modules.utils.build.SdkLevel;
/**
@@ -29,6 +31,7 @@ import com.android.modules.utils.build.SdkLevel;
*
* @hide
*/
+@MinSdk(Build.VERSION_CODES.S)
@SystemApi(client = Client.MODULE_LIBRARIES)
public class MediaFrameworkInitializer {
private MediaFrameworkInitializer() {
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index b29bed978a21..79e0d58cf495 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -26,6 +26,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -34,6 +35,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.annotation.MinSdk;
import java.io.FileNotFoundException;
import java.lang.annotation.Retention;
@@ -48,27 +50,23 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- MediaTranscodeManager provides an interface to the system's media transcoding service and can be
- used to transcode media files, e.g. transcoding a video from HEVC to AVC.
+ Android 12 introduces Compatible media transcoding feature. See
+ <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
+ Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media
+ transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to
+ AVC.
<h3>Transcoding Types</h3>
<h4>Video Transcoding</h4>
- When transcoding a video file, the video file could be of any of the following types:
- <ul>
- <li> Video file with single video track. </li>
- <li> Video file with multiple video track. </li>
- <li> Video file with multiple video tracks and audio tracks. </li>
- <li> Video file with video/audio tracks and metadata track. Note that metadata track will be passed
- through only if it could be recognized by {@link MediaExtractor}.
- TODO(hkuang): Finalize the metadata track behavior. </li>
- </ul>
+ When transcoding a video file, the video track will be transcoded based on the desired track format
+ and the audio track will be pass through without any modification.
<p class=note>
- Note that currently only support transcoding video file in mp4 format.
+ Note that currently only support transcoding video file in mp4 format and with single video track.
<h3>Transcoding Request</h3>
<p>
To transcode a media file, first create a {@link TranscodingRequest} through its builder class
- {@link TranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
+ {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
{@link MediaTranscodeManager#enqueueRequest(
TranscodingRequest, Executor, OnTranscodingFinishedListener)}
TranscodeRequest are processed based on client process's priority and request priority. When a
@@ -80,25 +78,12 @@ import java.util.concurrent.Executors;
Here is an example where <code>Builder</code> is used to specify all parameters
<pre class=prettyprint>
- TranscodingRequest request =
- new TranscodingRequest.Builder()
- .setSourceUri(srcUri)
- .setDestinationUri(dstUri)
- .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
- .setPriority(REALTIME)
- .setVideoTrackFormat(videoFormat)
- .build();
+ VideoTranscodingRequest request =
+ new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build();
}</pre>
-
- TODO(hkuang): Add architecture diagram showing the transcoding service and api.
- TODO(hkuang): Add sample code when API is settled.
- TODO(hkuang): Clarify whether multiple video tracks is supported or not.
- TODO(hkuang): Clarify whether image/audio transcoding is supported or not.
- TODO(hkuang): Clarify what will happen if there is unrecognized track in the source.
- TODO(hkuang): Clarify whether supports scaling.
- TODO(hkuang): Clarify whether supports framerate conversion.
@hide
*/
+@MinSdk(Build.VERSION_CODES.S)
@SystemApi
public final class MediaTranscodeManager {
private static final String TAG = "MediaTranscodeManager";
@@ -113,68 +98,6 @@ public final class MediaTranscodeManager {
private static final float BPP = 0.25f;
/**
- * Default transcoding type.
- * @hide
- */
- public static final int TRANSCODING_TYPE_UNKNOWN = 0;
-
- /**
- * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video file.
- * <p>Note that currently only support transcoding video file in mp4 format.
- */
- public static final int TRANSCODING_TYPE_VIDEO = 1;
-
- /**
- * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image file.
- * @hide
- */
- public static final int TRANSCODING_TYPE_IMAGE = 2;
-
- /** @hide */
- @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
- TRANSCODING_TYPE_UNKNOWN,
- TRANSCODING_TYPE_VIDEO,
- TRANSCODING_TYPE_IMAGE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TranscodingType {}
-
- /**
- * Default value.
- * @hide
- */
- public static final int PRIORITY_UNKNOWN = 0;
- /**
- * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
- * client wants the transcoding result as soon as possible.
- * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
- * performance penalty due to resource reallocation to prioritize the sessions with higher
- * priority.
- * TODO(hkuang): Add more description of this when priority is finalized.
- */
- public static final int PRIORITY_REALTIME = 1;
-
- /**
- * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not need
- * the transcoding result as soon as possible.
- * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to
- * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
- * delay of the transcoding result.
- * @hide
- * TODO(hkuang): Add more description of this when priority is finalized.
- */
- public static final int PRIORITY_OFFLINE = 2;
-
- /** @hide */
- @IntDef(prefix = {"PRIORITY_"}, value = {
- PRIORITY_UNKNOWN,
- PRIORITY_REALTIME,
- PRIORITY_OFFLINE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TranscodingPriority {}
-
- /**
* Listener that gets notified when a transcoding operation has finished.
* This listener gets notified regardless of how the operation finished. It is up to the
* listener implementation to check the result and take appropriate action.
@@ -217,7 +140,8 @@ public final class MediaTranscodeManager {
// Updates the session status and result.
session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
- TranscodingSession.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS,
+ TranscodingSession.ERROR_NONE);
// Notifies client the session is done.
if (session.mListener != null && session.mListenerExecutor != null) {
@@ -241,7 +165,7 @@ public final class MediaTranscodeManager {
// Updates the session status and result.
session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
- TranscodingSession.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR, errorCode);
// Notifies client the session failed.
if (session.mListener != null && session.mListenerExecutor != null) {
@@ -330,7 +254,8 @@ public final class MediaTranscodeManager {
if (session.getStatus() == TranscodingSession.STATUS_RUNNING) {
session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
- TranscodingSession.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR,
+ TranscodingSession.ERROR_SERVICE_DIED);
// Remove the session from pending sessions.
mPendingTranscodingSessions.remove(entry.getKey());
@@ -498,7 +423,79 @@ public final class MediaTranscodeManager {
}
}
- public static final class TranscodingRequest {
+ /**
+ * Abstract base class for all the TranscodingRequest.
+ * <p> TranscodingRequest encapsulates the desired configuration for the transcoding.
+ */
+ public abstract static class TranscodingRequest {
+ /**
+ *
+ * Default transcoding type.
+ * @hide
+ */
+ public static final int TRANSCODING_TYPE_UNKNOWN = 0;
+
+ /**
+ * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video.
+ * <p>Note that currently only support transcoding video file in mp4 format.
+ * @hide
+ */
+ public static final int TRANSCODING_TYPE_VIDEO = 1;
+
+ /**
+ * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image.
+ * @hide
+ */
+ public static final int TRANSCODING_TYPE_IMAGE = 2;
+
+ /** @hide */
+ @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
+ TRANSCODING_TYPE_UNKNOWN,
+ TRANSCODING_TYPE_VIDEO,
+ TRANSCODING_TYPE_IMAGE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TranscodingType {}
+
+ /**
+ * Default value.
+ *
+ * @hide
+ */
+ public static final int PRIORITY_UNKNOWN = 0;
+ /**
+ * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
+ * client wants the transcoding result as soon as possible.
+ * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
+ * performance penalty due to resource reallocation to prioritize the sessions with higher
+ * priority.
+ *
+ * @hide
+ */
+ public static final int PRIORITY_REALTIME = 1;
+
+ /**
+ * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not
+ * need the transcoding result as soon as possible.
+ * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set
+ * to
+ * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
+ * delay of the transcoding result.
+ *
+ * @hide
+ *
+ */
+ public static final int PRIORITY_OFFLINE = 2;
+
+ /** @hide */
+ @IntDef(prefix = {"PRIORITY_"}, value = {
+ PRIORITY_UNKNOWN,
+ PRIORITY_REALTIME,
+ PRIORITY_OFFLINE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TranscodingPriority {}
+
/** Uri of the source media file. */
private @NonNull Uri mSourceUri;
@@ -532,22 +529,6 @@ public final class MediaTranscodeManager {
private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
/**
- * Desired output video format of the destination file.
- * <p> If this is null, source file's video track will be passed through and copied to the
- * destination file.
- * <p>
- */
- private @Nullable MediaFormat mVideoTrackFormat = null;
-
- /**
- * Desired output audio format of the destination file.
- * <p> If this is null, source file's audio track will be passed through and copied to the
- * destination file.
- * @hide
- */
- private @Nullable MediaFormat mAudioTrackFormat = null;
-
- /**
* Desired image format for the destination file.
* <p> If this is null, source file's image track will be passed through and copied to the
* destination file.
@@ -558,6 +539,12 @@ public final class MediaTranscodeManager {
@VisibleForTesting
private TranscodingTestConfig mTestConfig = null;
+ /**
+ * Prevent public constructor access.
+ */
+ /* package private */ TranscodingRequest() {
+ }
+
private TranscodingRequest(Builder b) {
mSourceUri = b.mSourceUri;
mSourceFileDescriptor = b.mSourceFileDescriptor;
@@ -567,13 +554,13 @@ public final class MediaTranscodeManager {
mClientPid = b.mClientPid;
mPriority = b.mPriority;
mType = b.mType;
- mVideoTrackFormat = b.mVideoTrackFormat;
- mAudioTrackFormat = b.mAudioTrackFormat;
- mImageFormat = b.mImageFormat;
mTestConfig = b.mTestConfig;
}
- /** Return the type of the transcoding. */
+ /**
+ * Return the type of the transcoding.
+ * @hide
+ */
@TranscodingType
public int getType() {
return mType;
@@ -619,22 +606,16 @@ public final class MediaTranscodeManager {
return mDestinationFileDescriptor;
}
- /** Return priority of the transcoding. */
+ /**
+ * Return priority of the transcoding.
+ * @hide
+ */
@TranscodingPriority
public int getPriority() {
return mPriority;
}
/**
- * Return the video track format of the transcoding.
- * This will be null is the transcoding is not for video transcoding.
- */
- @Nullable
- public MediaFormat getVideoTrackFormat() {
- return mVideoTrackFormat;
- }
-
- /**
* Return TestConfig of the transcoding.
* @hide
*/
@@ -643,6 +624,8 @@ public final class MediaTranscodeManager {
return mTestConfig;
}
+ abstract void writeFormatToParcel(TranscodingRequestParcel parcel);
+
/* Writes the TranscodingRequest to a parcel. */
private TranscodingRequestParcel writeToParcel(@NonNull Context context) {
TranscodingRequestParcel parcel = new TranscodingRequestParcel();
@@ -666,7 +649,7 @@ public final class MediaTranscodeManager {
}
parcel.clientPackageName = packageName;
}
- parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
+ writeFormatToParcel(parcel);
if (mTestConfig != null) {
parcel.isForTesting = true;
parcel.testConfig = mTestConfig;
@@ -674,71 +657,12 @@ public final class MediaTranscodeManager {
return parcel;
}
- /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
- private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
- if (format == null) {
- throw new IllegalArgumentException("Invalid MediaFormat");
- }
-
- TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
-
- if (format.containsKey(MediaFormat.KEY_MIME)) {
- String mime = format.getString(MediaFormat.KEY_MIME);
- if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
- trackFormat.codecType = TranscodingVideoCodecType.kAvc;
- } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
- trackFormat.codecType = TranscodingVideoCodecType.kHevc;
- } else {
- throw new UnsupportedOperationException("Only support transcode to avc/hevc");
- }
- }
-
- if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
- int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
- if (bitrateBps <= 0) {
- throw new IllegalArgumentException("Bitrate must be larger than 0");
- }
- trackFormat.bitrateBps = bitrateBps;
- }
-
- if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
- MediaFormat.KEY_HEIGHT)) {
- int width = format.getInteger(MediaFormat.KEY_WIDTH);
- int height = format.getInteger(MediaFormat.KEY_HEIGHT);
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("Width and height must be larger than 0");
- }
- // TODO(hkuang): Validate the aspect ratio after adding scaling.
- trackFormat.width = width;
- trackFormat.height = height;
- }
-
- if (format.containsKey(MediaFormat.KEY_PROFILE)) {
- int profile = format.getInteger(MediaFormat.KEY_PROFILE);
- if (profile <= 0) {
- throw new IllegalArgumentException("Invalid codec profile");
- }
- // TODO(hkuang): Validate the profile according to codec type.
- trackFormat.profile = profile;
- }
-
- if (format.containsKey(MediaFormat.KEY_LEVEL)) {
- int level = format.getInteger(MediaFormat.KEY_LEVEL);
- if (level <= 0) {
- throw new IllegalArgumentException("Invalid codec level");
- }
- // TODO(hkuang): Validate the level according to codec type.
- trackFormat.level = level;
- }
-
- return trackFormat;
- }
-
/**
- * Builder class for {@link TranscodingRequest} objects.
- * Use this class to configure and create a <code>TranscodingRequest</code> instance.
+ * Builder to build a {@link TranscodingRequest} object.
+ *
+ * @param <T> The subclass to be built.
*/
- public static final class Builder {
+ abstract static class Builder<T extends Builder<T>> {
private @NonNull Uri mSourceUri;
private @NonNull Uri mDestinationUri;
private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null;
@@ -747,80 +671,68 @@ public final class MediaTranscodeManager {
private int mClientPid = -1;
private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
- private @Nullable MediaFormat mVideoTrackFormat;
- private @Nullable MediaFormat mAudioTrackFormat;
- private @Nullable MediaFormat mImageFormat;
private TranscodingTestConfig mTestConfig;
+ abstract T self();
+
/**
- * Specifies the uri of source media file.
+ * Creates a builder for building {@link TranscodingRequest}s.
*
* Client must set the source Uri. If client also provides the source fileDescriptor
* through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)},
* TranscodingSession will use the fd instead of calling back to the client to open the
* sourceUri.
+ *
+ *
+ * @param type The transcoding type.
* @param sourceUri Content uri for the source media file.
- * @return The same builder instance.
- * @throws IllegalArgumentException if Uri is null or empty.
+ * @param destinationUri Content uri for the destination media file.
+ *
*/
- @NonNull
- public Builder setSourceUri(@NonNull Uri sourceUri) {
+ private Builder(@TranscodingType int type, @NonNull Uri sourceUri,
+ @NonNull Uri destinationUri) {
+ mType = type;
+
if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) {
throw new IllegalArgumentException(
"You must specify a non-empty source Uri.");
}
mSourceUri = sourceUri;
- return this;
+
+ if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
+ throw new IllegalArgumentException(
+ "You must specify a non-empty destination Uri.");
+ }
+ mDestinationUri = destinationUri;
}
/**
* Specifies the fileDescriptor opened from the source media file.
*
* This call is optional. If the source fileDescriptor is provided, TranscodingSession
- * will use it directly instead of opening the uri from {@link #setSourceUri(Uri)}. It
- * is client's responsibility to make sure the fileDescriptor is opened from the source
- * uri.
+ * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}.
+ * It is client's responsibility to make sure the fileDescriptor is opened from the
+ * source uri.
* @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file.
* @return The same builder instance.
* @throws IllegalArgumentException if fileDescriptor is invalid.
*/
@NonNull
- public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) {
+ public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) {
if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
throw new IllegalArgumentException(
"Invalid source descriptor.");
}
mSourceFileDescriptor = fileDescriptor;
- return this;
- }
-
- /**
- * Specifies the uri of the destination media file.
- *
- * Client must set the destination Uri. If client also provides the destination
- * fileDescriptor through {@link #setDestinationFileDescriptor(ParcelFileDescriptor)},
- * TranscodingSession will use the fd instead of calling back to the client to open the
- * destinationUri.
- * @param destinationUri Content uri for the destination media file.
- * @return The same builder instance.
- * @throws IllegalArgumentException if Uri is null or empty.
- */
- @NonNull
- public Builder setDestinationUri(@NonNull Uri destinationUri) {
- if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
- throw new IllegalArgumentException(
- "You must specify a non-empty destination Uri.");
- }
- mDestinationUri = destinationUri;
- return this;
+ return self();
}
/**
* Specifies the fileDescriptor opened from the destination media file.
*
* This call is optional. If the destination fileDescriptor is provided,
- * TranscodingSession will use it directly instead of opening the uri from
- * {@link #setDestinationUri(Uri)} upon transcoding starts. It is client's
+ * TranscodingSession will use it directly instead of opening the source uri from
+ * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's
* responsibility to make sure the fileDescriptor is opened from the destination uri.
* @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media
* file.
@@ -828,46 +740,54 @@ public final class MediaTranscodeManager {
* @throws IllegalArgumentException if fileDescriptor is invalid.
*/
@NonNull
- public Builder setDestinationFileDescriptor(
+ public T setDestinationFileDescriptor(
@NonNull ParcelFileDescriptor fileDescriptor) {
if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
throw new IllegalArgumentException(
"Invalid destination descriptor.");
}
mDestinationFileDescriptor = fileDescriptor;
- return this;
+ return self();
}
/**
* Specify the UID of the client that this request is for.
+ * <p>
+ * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
+ * pid. Note that the permission check happens on the service side upon starting the
+ * transcoding. If the client does not have the permission, the transcoding will fail.
+ *
* @param uid client Uid.
* @return The same builder instance.
* @throws IllegalArgumentException if uid is invalid.
- * TODO(hkuang): Check the permission if it is allowed.
*/
@NonNull
- public Builder setClientUid(int uid) {
+ public T setClientUid(int uid) {
if (uid < 0) {
throw new IllegalArgumentException("Invalid Uid");
}
mClientUid = uid;
- return this;
+ return self();
}
/**
- * Specify the PID of the client that this request is for.
+ * Specify the pid of the client that this request is for.
+ * <p>
+ * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
+ * pid. Note that the permission check happens on the service side upon starting the
+ * transcoding. If the client does not have the permission, the transcoding will fail.
+ *
* @param pid client Pid.
* @return The same builder instance.
* @throws IllegalArgumentException if pid is invalid.
- * TODO(hkuang): Check the permission if it is allowed.
*/
@NonNull
- public Builder setClientPid(int pid) {
+ public T setClientPid(int pid) {
if (pid < 0) {
throw new IllegalArgumentException("Invalid pid");
}
mClientPid = pid;
- return this;
+ return self();
}
/**
@@ -876,64 +796,15 @@ public final class MediaTranscodeManager {
* @param priority Must be one of the {@code PRIORITY_*}
* @return The same builder instance.
* @throws IllegalArgumentException if flags is invalid.
+ * @hide
*/
@NonNull
- public Builder setPriority(@TranscodingPriority int priority) {
+ public T setPriority(@TranscodingPriority int priority) {
if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) {
throw new IllegalArgumentException("Invalid priority: " + priority);
}
mPriority = priority;
- return this;
- }
-
- /**
- * Specifies the type of transcoding.
- * <p> Clients must provide the source and destination that corresponds to the
- * transcoding type.
- *
- * @param type Must be one of the {@code TRANSCODING_TYPE_*}
- * @return The same builder instance.
- * @throws IllegalArgumentException if flags is invalid.
- */
- @NonNull
- public Builder setType(@TranscodingType int type) {
- if (type != TRANSCODING_TYPE_VIDEO && type != TRANSCODING_TYPE_IMAGE) {
- throw new IllegalArgumentException("Invalid transcoding type");
- }
- mType = type;
- return this;
- }
-
- /**
- * Specifies the desired video track format in the destination media file.
- * <p>Client could only specify the settings that matters to them, e.g. codec format or
- * bitrate. And by default, transcoding will preserve the original video's
- * settings(bitrate, framerate, resolution) if not provided.
- * <p>Note that some settings may silently fail to apply if the device does not
- * support them.
- * TODO(hkuang): Add MediaTranscodeUtil to help client generate transcoding setting.
- * TODO(hkuang): Add MediaTranscodeUtil to check if the setting is valid.
- *
- * @param videoFormat MediaFormat containing the settings that client wants override in
- * the original video's video track.
- * @return The same builder instance.
- * @throws IllegalArgumentException if videoFormat is invalid.
- */
- @NonNull
- public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
- if (videoFormat == null) {
- throw new IllegalArgumentException("videoFormat must not be null");
- }
-
- // Check if the MediaFormat is for video by looking at the MIME type.
- String mime = videoFormat.containsKey(MediaFormat.KEY_MIME)
- ? videoFormat.getString(MediaFormat.KEY_MIME) : null;
- if (mime == null || !mime.startsWith("video/")) {
- throw new IllegalArgumentException("Invalid video format: wrong mime type");
- }
-
- mVideoTrackFormat = videoFormat;
- return this;
+ return self();
}
/**
@@ -944,44 +815,9 @@ public final class MediaTranscodeManager {
*/
@VisibleForTesting
@NonNull
- public Builder setTestConfig(@NonNull TranscodingTestConfig config) {
+ public T setTestConfig(@NonNull TranscodingTestConfig config) {
mTestConfig = config;
- return this;
- }
-
- /**
- * @return a new {@link TranscodingRequest} instance successfully initialized with all
- * the parameters set on this <code>Builder</code>.
- * @throws UnsupportedOperationException if the parameters set on the
- * <code>Builder</code> were incompatible, or if they are not supported by the
- * device.
- */
- @NonNull
- public TranscodingRequest build() {
- if (mSourceUri == null) {
- throw new UnsupportedOperationException("Source URI must not be null");
- }
-
- if (mDestinationUri == null) {
- throw new UnsupportedOperationException("Destination URI must not be null");
- }
-
- if (mPriority == PRIORITY_UNKNOWN) {
- throw new UnsupportedOperationException("Must specify transcoding priority");
- }
-
- // Only support video transcoding now.
- if (mType != TRANSCODING_TYPE_VIDEO) {
- throw new UnsupportedOperationException("Only supports video transcoding now");
- }
-
- // Must provide video track format for video transcoding.
- if (mType == TRANSCODING_TYPE_VIDEO && mVideoTrackFormat == null) {
- throw new UnsupportedOperationException(
- "Must provide video track format for video transcoding");
- }
-
- return new TranscodingRequest(this);
+ return self();
}
}
@@ -1196,6 +1032,206 @@ public final class MediaTranscodeManager {
}
/**
+ * VideoTranscodingRequest encapsulates the configuration for transcoding a video.
+ */
+ public static final class VideoTranscodingRequest extends TranscodingRequest {
+ /**
+ * Desired output video format of the destination file.
+ * <p> If this is null, source file's video track will be passed through and copied to the
+ * destination file.
+ */
+ private @Nullable MediaFormat mVideoTrackFormat = null;
+
+ /**
+ * Desired output audio format of the destination file.
+ * <p> If this is null, source file's audio track will be passed through and copied to the
+ * destination file.
+ */
+ private @Nullable MediaFormat mAudioTrackFormat = null;
+
+ private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) {
+ super(builder);
+ mVideoTrackFormat = builder.mVideoTrackFormat;
+ mAudioTrackFormat = builder.mAudioTrackFormat;
+ }
+
+ /**
+ * Return the video track format of the transcoding.
+ * This will be null if client has not specified the video track format.
+ */
+ @NonNull
+ public MediaFormat getVideoTrackFormat() {
+ return mVideoTrackFormat;
+ }
+
+ @Override
+ void writeFormatToParcel(TranscodingRequestParcel parcel) {
+ parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
+ }
+
+ /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
+ private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
+ if (format == null) {
+ throw new IllegalArgumentException("Invalid MediaFormat");
+ }
+
+ TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
+
+ if (format.containsKey(MediaFormat.KEY_MIME)) {
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
+ trackFormat.codecType = TranscodingVideoCodecType.kAvc;
+ } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
+ trackFormat.codecType = TranscodingVideoCodecType.kHevc;
+ } else {
+ throw new UnsupportedOperationException("Only support transcode to avc/hevc");
+ }
+ }
+
+ if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
+ int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
+ if (bitrateBps <= 0) {
+ throw new IllegalArgumentException("Bitrate must be larger than 0");
+ }
+ trackFormat.bitrateBps = bitrateBps;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
+ MediaFormat.KEY_HEIGHT)) {
+ int width = format.getInteger(MediaFormat.KEY_WIDTH);
+ int height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Width and height must be larger than 0");
+ }
+ // TODO: Validate the aspect ratio after adding scaling.
+ trackFormat.width = width;
+ trackFormat.height = height;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+ int profile = format.getInteger(MediaFormat.KEY_PROFILE);
+ if (profile <= 0) {
+ throw new IllegalArgumentException("Invalid codec profile");
+ }
+ // TODO: Validate the profile according to codec type.
+ trackFormat.profile = profile;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_LEVEL)) {
+ int level = format.getInteger(MediaFormat.KEY_LEVEL);
+ if (level <= 0) {
+ throw new IllegalArgumentException("Invalid codec level");
+ }
+ // TODO: Validate the level according to codec type.
+ trackFormat.level = level;
+ }
+
+ return trackFormat;
+ }
+
+ /**
+ * Builder class for {@link VideoTranscodingRequest}.
+ */
+ public static final class Builder extends
+ TranscodingRequest.Builder<VideoTranscodingRequest.Builder> {
+ /**
+ * Desired output video format of the destination file.
+ * <p> If this is null, source file's video track will be passed through and
+ * copied to the destination file.
+ */
+ private @Nullable MediaFormat mVideoTrackFormat = null;
+
+ /**
+ * Desired output audio format of the destination file.
+ * <p> If this is null, source file's audio track will be passed through and copied
+ * to the destination file.
+ */
+ private @Nullable MediaFormat mAudioTrackFormat = null;
+
+ /**
+ * Creates a builder for building {@link VideoTranscodingRequest}s.
+ *
+ * <p> Client could only specify the settings that matters to them, e.g. codec format or
+ * bitrate. And by default, transcoding will preserve the original video's settings
+ * (bitrate, framerate, resolution) if not provided.
+ * <p>Note that some settings may silently fail to apply if the device does not support
+ * them.
+ * @param sourceUri Content uri for the source media file.
+ * @param destinationUri Content uri for the destination media file.
+ * @param videoFormat MediaFormat containing the settings that client wants override in
+ * the original video's video track.
+ * @throws IllegalArgumentException if videoFormat is invalid.
+ */
+ public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri,
+ @NonNull MediaFormat videoFormat) {
+ super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri);
+ setVideoTrackFormat(videoFormat);
+ }
+
+ @Override
+ @NonNull
+ public Builder setClientUid(int uid) {
+ super.setClientUid(uid);
+ return self();
+ }
+
+ @Override
+ @NonNull
+ public Builder setClientPid(int pid) {
+ super.setClientPid(pid);
+ return self();
+ }
+
+ @Override
+ @NonNull
+ public Builder setSourceFileDescriptor(ParcelFileDescriptor fd) {
+ super.setSourceFileDescriptor(fd);
+ return self();
+ }
+
+ @Override
+ @NonNull
+ public Builder setDestinationFileDescriptor(ParcelFileDescriptor fd) {
+ super.setDestinationFileDescriptor(fd);
+ return self();
+ }
+
+ private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
+ if (videoFormat == null) {
+ throw new IllegalArgumentException("videoFormat must not be null");
+ }
+
+ // Check if the MediaFormat is for video by looking at the MIME type.
+ String mime = videoFormat.containsKey(MediaFormat.KEY_MIME)
+ ? videoFormat.getString(MediaFormat.KEY_MIME) : null;
+ if (mime == null || !mime.startsWith("video/")) {
+ throw new IllegalArgumentException("Invalid video format: wrong mime type");
+ }
+
+ mVideoTrackFormat = videoFormat;
+ }
+
+ /**
+ * @return a new {@link TranscodingRequest} instance successfully initialized
+ * with all the parameters set on this <code>Builder</code>.
+ * @throws UnsupportedOperationException if the parameters set on the
+ * <code>Builder</code> were incompatible, or
+ * if they are not supported by the
+ * device.
+ */
+ @NonNull
+ public VideoTranscodingRequest build() {
+ return new VideoTranscodingRequest(this);
+ }
+
+ @Override
+ VideoTranscodingRequest.Builder self() {
+ return this;
+ }
+ }
+ }
+
+ /**
* Handle to an enqueued transcoding operation. An instance of this class represents a single
* enqueued transcoding operation. The caller can use that instance to query the status or
* progress, and to get the result once the operation has completed.
@@ -1239,6 +1275,33 @@ public final class MediaTranscodeManager {
@Retention(RetentionPolicy.SOURCE)
public @interface Result {}
+
+ // The error code exposed here should be in sync with:
+ // frameworks/av/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
+ /** @hide */
+ @IntDef(prefix = { "TRANSCODING_SESSION_ERROR_" }, value = {
+ ERROR_NONE,
+ ERROR_DROPPED_BY_SERVICE,
+ ERROR_SERVICE_DIED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TranscodingSessionErrorCode{}
+ /**
+ * Constant indicating that no error occurred.
+ */
+ public static final int ERROR_NONE = 0;
+
+ /**
+ * Constant indicating that the session is dropped by Transcoding service due to hitting
+ * the limit, e.g. too many back to back transcoding happen in a short time frame.
+ */
+ public static final int ERROR_DROPPED_BY_SERVICE = 1;
+
+ /**
+ * Constant indicating the backing transcoding service is died. Client should enqueue the
+ * the request again.
+ */
+ public static final int ERROR_SERVICE_DIED = 2;
+
/** Listener that gets notified when the progress changes. */
@FunctionalInterface
public interface OnProgressUpdateListener {
@@ -1272,6 +1335,8 @@ public final class MediaTranscodeManager {
@GuardedBy("mLock")
private @Result int mResult = RESULT_NONE;
@GuardedBy("mLock")
+ private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
+ @GuardedBy("mLock")
private boolean mHasRetried = false;
// The original request that associated with this session.
private final TranscodingRequest mRequest;
@@ -1325,10 +1390,20 @@ public final class MediaTranscodeManager {
}
private void updateStatusAndResult(@Status int sessionStatus,
- @Result int sessionResult) {
+ @Result int sessionResult, @TranscodingSessionErrorCode int errorCode) {
synchronized (mLock) {
mStatus = sessionStatus;
mResult = sessionResult;
+ mErrorCode = errorCode;
+ }
+ }
+
+ /**
+ * Retrieve the error code associated with the RESULT_ERROR.
+ */
+ public @TranscodingSessionErrorCode int getErrorCode() {
+ synchronized (mLock) {
+ return mErrorCode;
}
}
diff --git a/api/Android.bp b/api/Android.bp
index 1d4698e7c512..4baf7c1d5836 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -347,3 +347,51 @@ genrule {
out: ["combined-removed-dex.txt"],
cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
}
+
+genrule {
+ name: "services-system-server-current.txt",
+ srcs: [
+ ":service-media-s{.system-server.api.txt}",
+ ":service-permission{.system-server.api.txt}",
+ ":non-updatable-system-server-current.txt",
+ ],
+ out: ["system-server-current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ dists: [
+ {
+ targets: ["droidcore"],
+ dir: "api",
+ dest: "system-server-current.txt",
+ },
+ {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "merge-android.txt",
+ },
+ ],
+}
+
+genrule {
+ name: "services-system-server-removed.txt",
+ srcs: [
+ ":service-media-s{.system-server.removed-api.txt}",
+ ":service-permission{.system-server.removed-api.txt}",
+ ":non-updatable-system-server-removed.txt",
+ ],
+ out: ["system-server-removed.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ dists: [
+ {
+ targets: ["droidcore"],
+ dir: "api",
+ dest: "system-server-removed.txt",
+ },
+ {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "merge-removed.txt",
+ },
+ ],
+}
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 4e5b3bac5713..0eff83c99282 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -22,13 +22,9 @@ cc_binary {
multilib: {
lib32: {
- // TODO(b/142944043): Remove version script when libsigchain is a DSO.
- version_script: "version-script32.txt",
suffix: "32",
},
lib64: {
- // TODO(b/142944043): Remove version script when libsigchain is a DSO.
- version_script: "version-script64.txt",
suffix: "64",
},
},
@@ -43,6 +39,13 @@ cc_binary {
"libhidlbase",
"liblog",
"libnativeloader",
+
+ // Even though app_process doesn't call into libsigchain, we need to
+ // make sure it's in the DT list of app_process, as we want all code
+ // in app_process and the libraries it loads to find libsigchain
+ // symbols before libc symbols.
+ "libsigchain",
+
"libutils",
// This is a list of libraries that need to be included in order to avoid
@@ -52,8 +55,6 @@ cc_binary {
"libwilhelm",
],
- whole_static_libs: ["libsigchain"],
-
compile_multilib: "both",
cflags: [
diff --git a/cmds/app_process/version-script32.txt b/cmds/app_process/version-script32.txt
deleted file mode 100644
index 70810e0b7173..000000000000
--- a/cmds/app_process/version-script32.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-global:
- EnsureFrontOfChain;
- AddSpecialSignalHandlerFn;
- RemoveSpecialSignalHandlerFn;
- SkipAddSignalHandler;
- bsd_signal;
- sigaction;
- sigaction64;
- signal;
- sigprocmask;
- sigprocmask64;
-local:
- *;
-};
diff --git a/cmds/app_process/version-script64.txt b/cmds/app_process/version-script64.txt
deleted file mode 100644
index 7bcd76b50f87..000000000000
--- a/cmds/app_process/version-script64.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-global:
- EnsureFrontOfChain;
- AddSpecialSignalHandlerFn;
- RemoveSpecialSignalHandlerFn;
- SkipAddSignalHandler;
- sigaction;
- sigaction64;
- signal;
- sigprocmask;
- sigprocmask64;
-local:
- *;
-};
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 260c8a47ea3c..f5bee6c0c724 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -258,7 +258,7 @@ public final class Sm {
public void runDisableAppDataIsolation() throws RemoteException {
if (!SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
throw new IllegalStateException("Storage app data isolation is not enabled.");
}
final String pkgName = nextArg();
diff --git a/config/preloaded-classes b/config/preloaded-classes
index c6ec37690d88..7c3fd8ce8aea 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1844,11 +1844,9 @@ android.database.sqlite.SqliteWrapper
android.ddm.DdmHandleAppName$Names
android.ddm.DdmHandleAppName
android.ddm.DdmHandleExit
-android.ddm.DdmHandleHeap
android.ddm.DdmHandleHello
android.ddm.DdmHandleNativeHeap
android.ddm.DdmHandleProfiling
-android.ddm.DdmHandleThread
android.ddm.DdmHandleViewDebug
android.ddm.DdmRegister
android.debug.AdbManager
diff --git a/core/api/current.txt b/core/api/current.txt
index ba6f4409306d..a769493928a3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -99,6 +99,7 @@ package android {
field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
+ field public static final String MANAGE_MEDIA = "android.permission.MANAGE_MEDIA";
field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS";
field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -329,6 +330,7 @@ package android {
field public static final int apiKey = 16843281; // 0x1010211
field public static final int appCategory = 16844101; // 0x1010545
field public static final int appComponentFactory = 16844154; // 0x101057a
+ field public static final int attributionTags = 16844353; // 0x1010641
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -506,7 +508,7 @@ package android {
field public static final int dashGap = 16843175; // 0x10101a7
field public static final int dashWidth = 16843174; // 0x10101a6
field public static final int data = 16842798; // 0x101002e
- field public static final int dataExtractionRules = 16844350; // 0x101063e
+ field public static final int dataExtractionRules = 16844349; // 0x101063d
field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -528,8 +530,8 @@ package android {
field public static final int detailSocialSummary = 16843428; // 0x10102a4
field public static final int detailsElementBackground = 16843598; // 0x101034e
field public static final int dial = 16843010; // 0x1010102
- field public static final int dialTint = 16844342; // 0x1010636
- field public static final int dialTintMode = 16844343; // 0x1010637
+ field public static final int dialTint = 16844341; // 0x1010635
+ field public static final int dialTintMode = 16844342; // 0x1010636
field public static final int dialogCornerRadius = 16844145; // 0x1010571
field public static final int dialogIcon = 16843252; // 0x10101f4
field public static final int dialogLayout = 16843255; // 0x10101f7
@@ -727,14 +729,14 @@ package android {
field public static final int groupIndicator = 16843019; // 0x101010b
field public static final int gwpAsanMode = 16844310; // 0x1010616
field public static final int hand_hour = 16843011; // 0x1010103
- field public static final int hand_hourTint = 16844344; // 0x1010638
- field public static final int hand_hourTintMode = 16844345; // 0x1010639
+ field public static final int hand_hourTint = 16844343; // 0x1010637
+ field public static final int hand_hourTintMode = 16844344; // 0x1010638
field public static final int hand_minute = 16843012; // 0x1010104
- field public static final int hand_minuteTint = 16844346; // 0x101063a
- field public static final int hand_minuteTintMode = 16844347; // 0x101063b
+ field public static final int hand_minuteTint = 16844345; // 0x1010639
+ field public static final int hand_minuteTintMode = 16844346; // 0x101063a
field public static final int hand_second = 16844323; // 0x1010623
- field public static final int hand_secondTint = 16844348; // 0x101063c
- field public static final int hand_secondTintMode = 16844349; // 0x101063d
+ field public static final int hand_secondTint = 16844347; // 0x101063b
+ field public static final int hand_secondTintMode = 16844348; // 0x101063c
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -818,7 +820,7 @@ package android {
field public static final int installLocation = 16843447; // 0x10102b7
field public static final int interactiveUiTimeout = 16844181; // 0x1010595
field public static final int interpolator = 16843073; // 0x1010141
- field public static final int isAccessibilityTool = 16844353; // 0x1010641
+ field public static final int isAccessibilityTool = 16844352; // 0x1010640
field public static final int isAlwaysSyncable = 16843571; // 0x1010333
field public static final int isAsciiCapable = 16843753; // 0x10103e9
field public static final int isAuxiliary = 16843647; // 0x101037f
@@ -970,8 +972,8 @@ package android {
field public static final int maxLines = 16843091; // 0x1010153
field public static final int maxLongVersionCode = 16844163; // 0x1010583
field public static final int maxRecents = 16843846; // 0x1010446
- field public static final int maxResizeHeight = 16844339; // 0x1010633
- field public static final int maxResizeWidth = 16844338; // 0x1010632
+ field public static final int maxResizeHeight = 16844338; // 0x1010632
+ field public static final int maxResizeWidth = 16844337; // 0x1010631
field public static final int maxRows = 16843059; // 0x1010133
field public static final int maxSdkVersion = 16843377; // 0x1010271
field public static final int maxWidth = 16843039; // 0x101011f
@@ -1074,7 +1076,7 @@ package android {
field public static final int panelTextAppearance = 16842850; // 0x1010062
field public static final int parentActivityName = 16843687; // 0x10103a7
field @Deprecated public static final int password = 16843100; // 0x101015c
- field public static final int passwordsActivity = 16844351; // 0x101063f
+ field public static final int passwordsActivity = 16844350; // 0x101063e
field public static final int path = 16842794; // 0x101002a
field public static final int pathAdvancedPattern = 16844320; // 0x1010620
field public static final int pathData = 16843781; // 0x1010405
@@ -1199,7 +1201,6 @@ package android {
field public static final int right = 16843183; // 0x10101af
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
field public static final int ringtoneType = 16843257; // 0x10101f9
- field public static final int rippleStyle = 16844337; // 0x1010631
field public static final int rollbackDataPolicy = 16844311; // 0x1010617
field public static final int rotation = 16843558; // 0x1010326
field public static final int rotationAnimation = 16844090; // 0x101053a
@@ -1261,7 +1262,7 @@ package android {
field public static final int segmentedButtonStyle = 16843568; // 0x1010330
field public static final int selectAllOnFocus = 16843102; // 0x101015e
field public static final int selectable = 16843238; // 0x10101e6
- field public static final int selectableAsDefault = 16844352; // 0x1010640
+ field public static final int selectableAsDefault = 16844351; // 0x101063f
field public static final int selectableItemBackground = 16843534; // 0x101030e
field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
field @Deprecated public static final int selectedDateVerticalBar = 16843591; // 0x1010347
@@ -1394,6 +1395,7 @@ package android {
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
+ field public static final int suppressesSpellChecker = 16844354; // 0x1010642
field public static final int switchMinWidth = 16843632; // 0x1010370
field public static final int switchPadding = 16843633; // 0x1010371
field public static final int switchPreferenceStyle = 16843629; // 0x101036d
@@ -1408,8 +1410,8 @@ package android {
field public static final int tabWidgetStyle = 16842883; // 0x1010083
field public static final int tag = 16842961; // 0x10100d1
field public static final int targetActivity = 16843266; // 0x1010202
- field public static final int targetCellHeight = 16844341; // 0x1010635
- field public static final int targetCellWidth = 16844340; // 0x1010634
+ field public static final int targetCellHeight = 16844340; // 0x1010634
+ field public static final int targetCellWidth = 16844339; // 0x1010633
field public static final int targetClass = 16842799; // 0x101002f
field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
field public static final int targetId = 16843740; // 0x10103dc
@@ -1678,7 +1680,7 @@ package android {
field public static final int windowSplashScreenAnimationDuration = 16844334; // 0x101062e
field public static final int windowSplashScreenBackground = 16844332; // 0x101062c
field public static final int windowSplashScreenBrandingImage = 16844335; // 0x101062f
- field public static final int windowSplashscreenContent = 16844132; // 0x1010564
+ field @Deprecated public static final int windowSplashscreenContent = 16844132; // 0x1010564
field @Deprecated public static final int windowSwipeToDismiss = 16843763; // 0x10103f3
field public static final int windowTitleBackgroundStyle = 16842844; // 0x101005c
field public static final int windowTitleSize = 16842842; // 0x101005a
@@ -3026,6 +3028,7 @@ package android.accessibilityservice {
field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
+ field public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; // 0xf
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
@@ -3855,7 +3858,7 @@ package android.app {
method @Deprecated public void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction);
}
- public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
ctor public Activity();
method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void closeContextMenu();
@@ -4349,8 +4352,8 @@ package android.app {
method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
- method public void setExact(int, long, android.app.PendingIntent);
- method public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
+ method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, android.app.PendingIntent);
+ method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
method public void setRepeating(int, long, long, android.app.PendingIntent);
@@ -4771,9 +4774,9 @@ package android.app {
}
public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
- ctor public Dialog(@NonNull android.content.Context);
- ctor public Dialog(@NonNull android.content.Context, @StyleRes int);
- ctor protected Dialog(@NonNull android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener);
+ ctor public Dialog(@NonNull @UiContext android.content.Context);
+ ctor public Dialog(@NonNull @UiContext android.content.Context, @StyleRes int);
+ ctor protected Dialog(@NonNull @UiContext android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener);
method public void addContentView(@NonNull android.view.View, @Nullable android.view.ViewGroup.LayoutParams);
method public void cancel();
method public void closeOptionsMenu();
@@ -4787,7 +4790,7 @@ package android.app {
method public boolean dispatchTrackballEvent(@NonNull android.view.MotionEvent);
method public <T extends android.view.View> T findViewById(@IdRes int);
method @Nullable public android.app.ActionBar getActionBar();
- method @NonNull public final android.content.Context getContext();
+ method @NonNull @UiContext public final android.content.Context getContext();
method @Nullable public android.view.View getCurrentFocus();
method @NonNull public android.view.LayoutInflater getLayoutInflater();
method @Nullable public final android.app.Activity getOwnerActivity();
@@ -8254,13 +8257,13 @@ package android.app.usage {
}
public class NetworkStatsManager {
- method public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
- method public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
- method public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
- method public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback);
method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback);
@@ -8491,7 +8494,7 @@ package android.appwidget {
field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
- field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3; // 0x3
+ field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; // 0x4
field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
field public int autoAdvanceViewId;
@@ -9908,6 +9911,7 @@ package android.content {
method public String getHtmlText();
method public android.content.Intent getIntent();
method public CharSequence getText();
+ method @Nullable public android.view.textclassifier.TextLinks getTextLinks();
method public android.net.Uri getUri();
}
@@ -9917,6 +9921,8 @@ package android.content {
method public static boolean compareMimeTypes(String, String);
method public int describeContents();
method public String[] filterMimeTypes(String);
+ method public int getClassificationStatus();
+ method @FloatRange(from=0.0, to=1.0) public float getConfidenceScore(@NonNull String);
method public android.os.PersistableBundle getExtras();
method public CharSequence getLabel();
method public String getMimeType(int);
@@ -9926,6 +9932,9 @@ package android.content {
method public boolean isStyledText();
method public void setExtras(android.os.PersistableBundle);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLASSIFICATION_COMPLETE = 3; // 0x3
+ field public static final int CLASSIFICATION_NOT_COMPLETE = 1; // 0x1
+ field public static final int CLASSIFICATION_NOT_PERFORMED = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
field public static final String MIMETYPE_TEXT_HTML = "text/html";
field public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
@@ -10346,10 +10355,10 @@ package android.content {
method @NonNull public android.content.Context createContext(@NonNull android.content.ContextParams);
method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.Context createDeviceProtectedStorageContext();
- method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
+ method @DisplayContext public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
- method @NonNull public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle);
+ method @NonNull @UiContext public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
+ method @NonNull @UiContext public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle);
method public abstract String[] databaseList();
method public abstract boolean deleteDatabase(String);
method public abstract boolean deleteFile(String);
@@ -10501,6 +10510,7 @@ package android.content {
field public static final String DEVICE_POLICY_SERVICE = "device_policy";
field public static final String DISPLAY_HASH_SERVICE = "display_hash";
field public static final String DISPLAY_SERVICE = "display";
+ field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
field public static final String DOWNLOAD_SERVICE = "download";
field public static final String DROPBOX_SERVICE = "dropbox";
field public static final String EUICC_SERVICE = "euicc";
@@ -10514,7 +10524,7 @@ package android.content {
field public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final String KEYGUARD_SERVICE = "keyguard";
field public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
- field public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+ field @UiContext public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
field public static final String LOCATION_SERVICE = "location";
field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
field public static final String MEDIA_METRICS_SERVICE = "media_metrics";
@@ -10559,12 +10569,12 @@ package android.content {
field public static final String VIBRATOR_MANAGER_SERVICE = "vibrator_manager";
field public static final String VIBRATOR_SERVICE = "vibrator";
field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
- field public static final String WALLPAPER_SERVICE = "wallpaper";
+ field @UiContext public static final String WALLPAPER_SERVICE = "wallpaper";
field public static final String WIFI_AWARE_SERVICE = "wifiaware";
field public static final String WIFI_P2P_SERVICE = "wifip2p";
field public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
field public static final String WIFI_SERVICE = "wifi";
- field public static final String WINDOW_SERVICE = "window";
+ field @UiContext public static final String WINDOW_SERVICE = "window";
}
public final class ContextParams {
@@ -10952,6 +10962,7 @@ package android.content {
field public static final String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
+ field public static final String ACTION_MANAGE_UNUSED_APPS = "android.intent.action.MANAGE_UNUSED_APPS";
field public static final String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
field public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
field public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -16687,13 +16698,9 @@ package android.graphics.drawable {
public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
ctor public RippleDrawable(@NonNull android.content.res.ColorStateList, @Nullable android.graphics.drawable.Drawable, @Nullable android.graphics.drawable.Drawable);
method public int getRadius();
- method public int getRippleStyle();
method public void setColor(android.content.res.ColorStateList);
method public void setRadius(int);
- method public void setRippleStyle(int) throws java.lang.IllegalArgumentException;
field public static final int RADIUS_AUTO = -1; // 0xffffffff
- field public static final int STYLE_PATTERNED = 1; // 0x1
- field public static final int STYLE_SOLID = 0; // 0x0
}
public class RotateDrawable extends android.graphics.drawable.DrawableWrapper {
@@ -18431,12 +18438,12 @@ package android.hardware.camera2 {
}
public class MultiResolutionImageReader implements java.lang.AutoCloseable {
+ ctor public MultiResolutionImageReader(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
method public void close();
method protected void finalize();
method public void flush();
method @NonNull public android.hardware.camera2.params.MultiResolutionStreamInfo getStreamInfoForImageReader(@NonNull android.media.ImageReader);
method @NonNull public android.view.Surface getSurface();
- method @NonNull public static android.hardware.camera2.MultiResolutionImageReader newInstance(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
method public void setOnImageAvailableListener(@Nullable android.media.ImageReader.OnImageAvailableListener, @Nullable java.util.concurrent.Executor);
}
@@ -18547,10 +18554,10 @@ package android.hardware.camera2.params {
}
public class MultiResolutionStreamInfo {
- ctor public MultiResolutionStreamInfo(int, int, @NonNull String);
- method public int getHeight();
+ ctor public MultiResolutionStreamInfo(@IntRange(from=1) int, @IntRange(from=1) int, @NonNull String);
+ method @IntRange(from=1) public int getHeight();
method @NonNull public String getPhysicalCameraId();
- method public int getWidth();
+ method @IntRange(from=1) public int getWidth();
}
public final class OisSample {
@@ -19058,7 +19065,7 @@ package android.inputmethodservice {
method public void startInternalChanges();
}
- public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
+ @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
ctor public InputMethodService();
method @Deprecated public boolean enableHardwareAcceleration();
method public int getBackDisposition();
@@ -20569,6 +20576,7 @@ package android.media {
method @NonNull public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+ method @NonNull public android.media.AudioRecord.Builder setContext(@NonNull android.content.Context);
method @NonNull public android.media.AudioRecord.Builder setPrivacySensitive(boolean);
}
@@ -21892,6 +21900,7 @@ package android.media {
method @NonNull public java.util.List<byte[]> getOfflineLicenseKeySetIds();
method public int getOfflineLicenseState(@NonNull byte[]);
method public int getOpenSessionCount();
+ method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
method @NonNull public byte[] getPropertyByteArray(String);
method @NonNull public String getPropertyString(@NonNull String);
method @NonNull public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
@@ -22309,6 +22318,8 @@ package android.media {
field public static final String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
field public static final String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
field public static final String MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+ field public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
+ field public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
field public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
field public static final String MIMETYPE_AUDIO_OPUS = "audio/opus";
field public static final String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
@@ -22743,7 +22754,8 @@ package android.media {
}
public class MediaRecorder implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
- ctor public MediaRecorder();
+ ctor @Deprecated public MediaRecorder();
+ ctor public MediaRecorder(@NonNull android.content.Context);
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
@@ -26140,10 +26152,6 @@ package android.net {
ctor public NetworkSpecifier();
}
- public class ParseException extends java.lang.RuntimeException {
- field public String response;
- }
-
public abstract class PlatformVpnProfile {
method public final int getType();
method @NonNull public final String getTypeString();
@@ -26555,236 +26563,236 @@ package android.net.nsd {
package android.net.rtp {
- public class AudioCodec {
- method public static android.net.rtp.AudioCodec getCodec(int, String, String);
- method public static android.net.rtp.AudioCodec[] getCodecs();
- field public static final android.net.rtp.AudioCodec AMR;
- field public static final android.net.rtp.AudioCodec GSM;
- field public static final android.net.rtp.AudioCodec GSM_EFR;
- field public static final android.net.rtp.AudioCodec PCMA;
- field public static final android.net.rtp.AudioCodec PCMU;
- field public final String fmtp;
- field public final String rtpmap;
- field public final int type;
+ @Deprecated public class AudioCodec {
+ method @Deprecated public static android.net.rtp.AudioCodec getCodec(int, String, String);
+ method @Deprecated public static android.net.rtp.AudioCodec[] getCodecs();
+ field @Deprecated public static final android.net.rtp.AudioCodec AMR;
+ field @Deprecated public static final android.net.rtp.AudioCodec GSM;
+ field @Deprecated public static final android.net.rtp.AudioCodec GSM_EFR;
+ field @Deprecated public static final android.net.rtp.AudioCodec PCMA;
+ field @Deprecated public static final android.net.rtp.AudioCodec PCMU;
+ field @Deprecated public final String fmtp;
+ field @Deprecated public final String rtpmap;
+ field @Deprecated public final int type;
}
- public class AudioGroup {
+ @Deprecated public class AudioGroup {
ctor @Deprecated public AudioGroup();
- ctor public AudioGroup(@NonNull android.content.Context);
- method public void clear();
- method public int getMode();
- method public android.net.rtp.AudioStream[] getStreams();
- method public void sendDtmf(int);
- method public void setMode(int);
- field public static final int MODE_ECHO_SUPPRESSION = 3; // 0x3
- field public static final int MODE_MUTED = 1; // 0x1
- field public static final int MODE_NORMAL = 2; // 0x2
- field public static final int MODE_ON_HOLD = 0; // 0x0
- }
-
- public class AudioStream extends android.net.rtp.RtpStream {
- ctor public AudioStream(java.net.InetAddress) throws java.net.SocketException;
- method public android.net.rtp.AudioCodec getCodec();
- method public int getDtmfType();
- method public android.net.rtp.AudioGroup getGroup();
- method public final boolean isBusy();
- method public void join(android.net.rtp.AudioGroup);
- method public void setCodec(android.net.rtp.AudioCodec);
- method public void setDtmfType(int);
- }
-
- public class RtpStream {
- method public void associate(java.net.InetAddress, int);
- method public java.net.InetAddress getLocalAddress();
- method public int getLocalPort();
- method public int getMode();
- method public java.net.InetAddress getRemoteAddress();
- method public int getRemotePort();
- method public boolean isBusy();
- method public void release();
- method public void setMode(int);
- field public static final int MODE_NORMAL = 0; // 0x0
- field public static final int MODE_RECEIVE_ONLY = 2; // 0x2
- field public static final int MODE_SEND_ONLY = 1; // 0x1
+ ctor @Deprecated public AudioGroup(@NonNull android.content.Context);
+ method @Deprecated public void clear();
+ method @Deprecated public int getMode();
+ method @Deprecated public android.net.rtp.AudioStream[] getStreams();
+ method @Deprecated public void sendDtmf(int);
+ method @Deprecated public void setMode(int);
+ field @Deprecated public static final int MODE_ECHO_SUPPRESSION = 3; // 0x3
+ field @Deprecated public static final int MODE_MUTED = 1; // 0x1
+ field @Deprecated public static final int MODE_NORMAL = 2; // 0x2
+ field @Deprecated public static final int MODE_ON_HOLD = 0; // 0x0
+ }
+
+ @Deprecated public class AudioStream extends android.net.rtp.RtpStream {
+ ctor @Deprecated public AudioStream(java.net.InetAddress) throws java.net.SocketException;
+ method @Deprecated public android.net.rtp.AudioCodec getCodec();
+ method @Deprecated public int getDtmfType();
+ method @Deprecated public android.net.rtp.AudioGroup getGroup();
+ method @Deprecated public final boolean isBusy();
+ method @Deprecated public void join(android.net.rtp.AudioGroup);
+ method @Deprecated public void setCodec(android.net.rtp.AudioCodec);
+ method @Deprecated public void setDtmfType(int);
+ }
+
+ @Deprecated public class RtpStream {
+ method @Deprecated public void associate(java.net.InetAddress, int);
+ method @Deprecated public java.net.InetAddress getLocalAddress();
+ method @Deprecated public int getLocalPort();
+ method @Deprecated public int getMode();
+ method @Deprecated public java.net.InetAddress getRemoteAddress();
+ method @Deprecated public int getRemotePort();
+ method @Deprecated public boolean isBusy();
+ method @Deprecated public void release();
+ method @Deprecated public void setMode(int);
+ field @Deprecated public static final int MODE_NORMAL = 0; // 0x0
+ field @Deprecated public static final int MODE_RECEIVE_ONLY = 2; // 0x2
+ field @Deprecated public static final int MODE_SEND_ONLY = 1; // 0x1
}
}
package android.net.sip {
- public class SipAudioCall {
- ctor public SipAudioCall(android.content.Context, android.net.sip.SipProfile);
- method public void answerCall(int) throws android.net.sip.SipException;
- method public void attachCall(android.net.sip.SipSession, String) throws android.net.sip.SipException;
- method public void close();
- method public void continueCall(int) throws android.net.sip.SipException;
- method public void endCall() throws android.net.sip.SipException;
- method public android.net.sip.SipProfile getLocalProfile();
- method public android.net.sip.SipProfile getPeerProfile();
- method public int getState();
- method public void holdCall(int) throws android.net.sip.SipException;
- method public boolean isInCall();
- method public boolean isMuted();
- method public boolean isOnHold();
- method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
- method public void sendDtmf(int);
- method public void sendDtmf(int, android.os.Message);
- method public void setListener(android.net.sip.SipAudioCall.Listener);
- method public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
- method public void setSpeakerMode(boolean);
- method public void startAudio();
- method public void toggleMute();
- }
-
- public static class SipAudioCall.Listener {
- ctor public SipAudioCall.Listener();
- method public void onCallBusy(android.net.sip.SipAudioCall);
- method public void onCallEnded(android.net.sip.SipAudioCall);
- method public void onCallEstablished(android.net.sip.SipAudioCall);
- method public void onCallHeld(android.net.sip.SipAudioCall);
- method public void onCalling(android.net.sip.SipAudioCall);
- method public void onChanged(android.net.sip.SipAudioCall);
- method public void onError(android.net.sip.SipAudioCall, int, String);
- method public void onReadyToCall(android.net.sip.SipAudioCall);
- method public void onRinging(android.net.sip.SipAudioCall, android.net.sip.SipProfile);
- method public void onRingingBack(android.net.sip.SipAudioCall);
- }
-
- public class SipErrorCode {
- method public static String toString(int);
- field public static final int CLIENT_ERROR = -4; // 0xfffffffc
- field public static final int CROSS_DOMAIN_AUTHENTICATION = -11; // 0xfffffff5
- field public static final int DATA_CONNECTION_LOST = -10; // 0xfffffff6
- field public static final int INVALID_CREDENTIALS = -8; // 0xfffffff8
- field public static final int INVALID_REMOTE_URI = -6; // 0xfffffffa
- field public static final int IN_PROGRESS = -9; // 0xfffffff7
- field public static final int NO_ERROR = 0; // 0x0
- field public static final int PEER_NOT_REACHABLE = -7; // 0xfffffff9
- field public static final int SERVER_ERROR = -2; // 0xfffffffe
- field public static final int SERVER_UNREACHABLE = -12; // 0xfffffff4
- field public static final int SOCKET_ERROR = -1; // 0xffffffff
- field public static final int TIME_OUT = -5; // 0xfffffffb
- field public static final int TRANSACTION_TERMINTED = -3; // 0xfffffffd
- }
-
- public class SipException extends java.lang.Exception {
- ctor public SipException();
- ctor public SipException(String);
- ctor public SipException(String, Throwable);
- }
-
- public class SipManager {
- method public void close(String) throws android.net.sip.SipException;
- method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
- method public static String getCallId(android.content.Intent);
- method public static String getOfferSessionDescription(android.content.Intent);
- method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
- method public static boolean isApiSupported(android.content.Context);
- method public static boolean isIncomingCallIntent(android.content.Intent);
- method public boolean isOpened(String) throws android.net.sip.SipException;
- method public boolean isRegistered(String) throws android.net.sip.SipException;
- method public static boolean isSipWifiOnly(android.content.Context);
- method public static boolean isVoipSupported(android.content.Context);
- method public android.net.sip.SipAudioCall makeAudioCall(android.net.sip.SipProfile, android.net.sip.SipProfile, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
- method public android.net.sip.SipAudioCall makeAudioCall(String, String, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
- method public static android.net.sip.SipManager newInstance(android.content.Context);
- method public void open(android.net.sip.SipProfile) throws android.net.sip.SipException;
- method public void open(android.net.sip.SipProfile, android.app.PendingIntent, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
- method public void register(android.net.sip.SipProfile, int, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
- method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
- method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
- method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
- field public static final String EXTRA_CALL_ID = "android:sipCallID";
- field public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
- field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
- }
-
- public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
- method public int describeContents();
- method public String getAuthUserName();
- method public boolean getAutoRegistration();
- method public String getDisplayName();
- method public String getPassword();
- method public int getPort();
- method public String getProfileName();
- method public String getProtocol();
- method public String getProxyAddress();
- method public boolean getSendKeepAlive();
- method public String getSipDomain();
- method public String getUriString();
- method public String getUserName();
- method public void setCallingUid(int);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR;
- }
-
- public static class SipProfile.Builder {
- ctor public SipProfile.Builder(android.net.sip.SipProfile);
- ctor public SipProfile.Builder(String) throws java.text.ParseException;
- ctor public SipProfile.Builder(String, String) throws java.text.ParseException;
- method public android.net.sip.SipProfile build();
- method public android.net.sip.SipProfile.Builder setAuthUserName(String);
- method public android.net.sip.SipProfile.Builder setAutoRegistration(boolean);
- method public android.net.sip.SipProfile.Builder setDisplayName(String);
- method public android.net.sip.SipProfile.Builder setOutboundProxy(String);
- method public android.net.sip.SipProfile.Builder setPassword(String);
- method public android.net.sip.SipProfile.Builder setPort(int) throws java.lang.IllegalArgumentException;
- method public android.net.sip.SipProfile.Builder setProfileName(String);
- method public android.net.sip.SipProfile.Builder setProtocol(String) throws java.lang.IllegalArgumentException;
- method public android.net.sip.SipProfile.Builder setSendKeepAlive(boolean);
- }
-
- public interface SipRegistrationListener {
- method public void onRegistering(String);
- method public void onRegistrationDone(String, long);
- method public void onRegistrationFailed(String, int, String);
- }
-
- public final class SipSession {
- method public void answerCall(String, int);
- method public void changeCall(String, int);
- method public void endCall();
- method public String getCallId();
- method public String getLocalIp();
- method public android.net.sip.SipProfile getLocalProfile();
- method public android.net.sip.SipProfile getPeerProfile();
- method public int getState();
- method public boolean isInCall();
- method public void makeCall(android.net.sip.SipProfile, String, int);
- method public void register(int);
- method public void setListener(android.net.sip.SipSession.Listener);
- method public void unregister();
- }
-
- public static class SipSession.Listener {
- ctor public SipSession.Listener();
- method public void onCallBusy(android.net.sip.SipSession);
- method public void onCallChangeFailed(android.net.sip.SipSession, int, String);
- method public void onCallEnded(android.net.sip.SipSession);
- method public void onCallEstablished(android.net.sip.SipSession, String);
- method public void onCalling(android.net.sip.SipSession);
- method public void onError(android.net.sip.SipSession, int, String);
- method public void onRegistering(android.net.sip.SipSession);
- method public void onRegistrationDone(android.net.sip.SipSession, int);
- method public void onRegistrationFailed(android.net.sip.SipSession, int, String);
- method public void onRegistrationTimeout(android.net.sip.SipSession);
- method public void onRinging(android.net.sip.SipSession, android.net.sip.SipProfile, String);
- method public void onRingingBack(android.net.sip.SipSession);
- }
-
- public static class SipSession.State {
- method public static String toString(int);
- field public static final int DEREGISTERING = 2; // 0x2
- field public static final int INCOMING_CALL = 3; // 0x3
- field public static final int INCOMING_CALL_ANSWERING = 4; // 0x4
- field public static final int IN_CALL = 8; // 0x8
- field public static final int NOT_DEFINED = 101; // 0x65
- field public static final int OUTGOING_CALL = 5; // 0x5
- field public static final int OUTGOING_CALL_CANCELING = 7; // 0x7
- field public static final int OUTGOING_CALL_RING_BACK = 6; // 0x6
- field public static final int PINGING = 9; // 0x9
- field public static final int READY_TO_CALL = 0; // 0x0
- field public static final int REGISTERING = 1; // 0x1
+ @Deprecated public class SipAudioCall {
+ ctor @Deprecated public SipAudioCall(android.content.Context, android.net.sip.SipProfile);
+ method @Deprecated public void answerCall(int) throws android.net.sip.SipException;
+ method @Deprecated public void attachCall(android.net.sip.SipSession, String) throws android.net.sip.SipException;
+ method @Deprecated public void close();
+ method @Deprecated public void continueCall(int) throws android.net.sip.SipException;
+ method @Deprecated public void endCall() throws android.net.sip.SipException;
+ method @Deprecated public android.net.sip.SipProfile getLocalProfile();
+ method @Deprecated public android.net.sip.SipProfile getPeerProfile();
+ method @Deprecated public int getState();
+ method @Deprecated public void holdCall(int) throws android.net.sip.SipException;
+ method @Deprecated public boolean isInCall();
+ method @Deprecated public boolean isMuted();
+ method @Deprecated public boolean isOnHold();
+ method @Deprecated public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
+ method @Deprecated public void sendDtmf(int);
+ method @Deprecated public void sendDtmf(int, android.os.Message);
+ method @Deprecated public void setListener(android.net.sip.SipAudioCall.Listener);
+ method @Deprecated public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
+ method @Deprecated public void setSpeakerMode(boolean);
+ method @Deprecated public void startAudio();
+ method @Deprecated public void toggleMute();
+ }
+
+ @Deprecated public static class SipAudioCall.Listener {
+ ctor @Deprecated public SipAudioCall.Listener();
+ method @Deprecated public void onCallBusy(android.net.sip.SipAudioCall);
+ method @Deprecated public void onCallEnded(android.net.sip.SipAudioCall);
+ method @Deprecated public void onCallEstablished(android.net.sip.SipAudioCall);
+ method @Deprecated public void onCallHeld(android.net.sip.SipAudioCall);
+ method @Deprecated public void onCalling(android.net.sip.SipAudioCall);
+ method @Deprecated public void onChanged(android.net.sip.SipAudioCall);
+ method @Deprecated public void onError(android.net.sip.SipAudioCall, int, String);
+ method @Deprecated public void onReadyToCall(android.net.sip.SipAudioCall);
+ method @Deprecated public void onRinging(android.net.sip.SipAudioCall, android.net.sip.SipProfile);
+ method @Deprecated public void onRingingBack(android.net.sip.SipAudioCall);
+ }
+
+ @Deprecated public class SipErrorCode {
+ method @Deprecated public static String toString(int);
+ field @Deprecated public static final int CLIENT_ERROR = -4; // 0xfffffffc
+ field @Deprecated public static final int CROSS_DOMAIN_AUTHENTICATION = -11; // 0xfffffff5
+ field @Deprecated public static final int DATA_CONNECTION_LOST = -10; // 0xfffffff6
+ field @Deprecated public static final int INVALID_CREDENTIALS = -8; // 0xfffffff8
+ field @Deprecated public static final int INVALID_REMOTE_URI = -6; // 0xfffffffa
+ field @Deprecated public static final int IN_PROGRESS = -9; // 0xfffffff7
+ field @Deprecated public static final int NO_ERROR = 0; // 0x0
+ field @Deprecated public static final int PEER_NOT_REACHABLE = -7; // 0xfffffff9
+ field @Deprecated public static final int SERVER_ERROR = -2; // 0xfffffffe
+ field @Deprecated public static final int SERVER_UNREACHABLE = -12; // 0xfffffff4
+ field @Deprecated public static final int SOCKET_ERROR = -1; // 0xffffffff
+ field @Deprecated public static final int TIME_OUT = -5; // 0xfffffffb
+ field @Deprecated public static final int TRANSACTION_TERMINTED = -3; // 0xfffffffd
+ }
+
+ @Deprecated public class SipException extends java.lang.Exception {
+ ctor @Deprecated public SipException();
+ ctor @Deprecated public SipException(String);
+ ctor @Deprecated public SipException(String, Throwable);
+ }
+
+ @Deprecated public class SipManager {
+ method @Deprecated public void close(String) throws android.net.sip.SipException;
+ method @Deprecated public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
+ method @Deprecated public static String getCallId(android.content.Intent);
+ method @Deprecated public static String getOfferSessionDescription(android.content.Intent);
+ method @Deprecated public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
+ method @Deprecated public static boolean isApiSupported(android.content.Context);
+ method @Deprecated public static boolean isIncomingCallIntent(android.content.Intent);
+ method @Deprecated public boolean isOpened(String) throws android.net.sip.SipException;
+ method @Deprecated public boolean isRegistered(String) throws android.net.sip.SipException;
+ method @Deprecated public static boolean isSipWifiOnly(android.content.Context);
+ method @Deprecated public static boolean isVoipSupported(android.content.Context);
+ method @Deprecated public android.net.sip.SipAudioCall makeAudioCall(android.net.sip.SipProfile, android.net.sip.SipProfile, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
+ method @Deprecated public android.net.sip.SipAudioCall makeAudioCall(String, String, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
+ method @Deprecated public static android.net.sip.SipManager newInstance(android.content.Context);
+ method @Deprecated public void open(android.net.sip.SipProfile) throws android.net.sip.SipException;
+ method @Deprecated public void open(android.net.sip.SipProfile, android.app.PendingIntent, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+ method @Deprecated public void register(android.net.sip.SipProfile, int, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+ method @Deprecated public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+ method @Deprecated public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
+ method @Deprecated public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+ field @Deprecated public static final String EXTRA_CALL_ID = "android:sipCallID";
+ field @Deprecated public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
+ field @Deprecated public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
+ }
+
+ @Deprecated public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+ method @Deprecated public int describeContents();
+ method @Deprecated public String getAuthUserName();
+ method @Deprecated public boolean getAutoRegistration();
+ method @Deprecated public String getDisplayName();
+ method @Deprecated public String getPassword();
+ method @Deprecated public int getPort();
+ method @Deprecated public String getProfileName();
+ method @Deprecated public String getProtocol();
+ method @Deprecated public String getProxyAddress();
+ method @Deprecated public boolean getSendKeepAlive();
+ method @Deprecated public String getSipDomain();
+ method @Deprecated public String getUriString();
+ method @Deprecated public String getUserName();
+ method @Deprecated public void setCallingUid(int);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR;
+ }
+
+ @Deprecated public static class SipProfile.Builder {
+ ctor @Deprecated public SipProfile.Builder(android.net.sip.SipProfile);
+ ctor @Deprecated public SipProfile.Builder(String) throws java.text.ParseException;
+ ctor @Deprecated public SipProfile.Builder(String, String) throws java.text.ParseException;
+ method @Deprecated public android.net.sip.SipProfile build();
+ method @Deprecated public android.net.sip.SipProfile.Builder setAuthUserName(String);
+ method @Deprecated public android.net.sip.SipProfile.Builder setAutoRegistration(boolean);
+ method @Deprecated public android.net.sip.SipProfile.Builder setDisplayName(String);
+ method @Deprecated public android.net.sip.SipProfile.Builder setOutboundProxy(String);
+ method @Deprecated public android.net.sip.SipProfile.Builder setPassword(String);
+ method @Deprecated public android.net.sip.SipProfile.Builder setPort(int) throws java.lang.IllegalArgumentException;
+ method @Deprecated public android.net.sip.SipProfile.Builder setProfileName(String);
+ method @Deprecated public android.net.sip.SipProfile.Builder setProtocol(String) throws java.lang.IllegalArgumentException;
+ method @Deprecated public android.net.sip.SipProfile.Builder setSendKeepAlive(boolean);
+ }
+
+ @Deprecated public interface SipRegistrationListener {
+ method @Deprecated public void onRegistering(String);
+ method @Deprecated public void onRegistrationDone(String, long);
+ method @Deprecated public void onRegistrationFailed(String, int, String);
+ }
+
+ @Deprecated public final class SipSession {
+ method @Deprecated public void answerCall(String, int);
+ method @Deprecated public void changeCall(String, int);
+ method @Deprecated public void endCall();
+ method @Deprecated public String getCallId();
+ method @Deprecated public String getLocalIp();
+ method @Deprecated public android.net.sip.SipProfile getLocalProfile();
+ method @Deprecated public android.net.sip.SipProfile getPeerProfile();
+ method @Deprecated public int getState();
+ method @Deprecated public boolean isInCall();
+ method @Deprecated public void makeCall(android.net.sip.SipProfile, String, int);
+ method @Deprecated public void register(int);
+ method @Deprecated public void setListener(android.net.sip.SipSession.Listener);
+ method @Deprecated public void unregister();
+ }
+
+ @Deprecated public static class SipSession.Listener {
+ ctor @Deprecated public SipSession.Listener();
+ method @Deprecated public void onCallBusy(android.net.sip.SipSession);
+ method @Deprecated public void onCallChangeFailed(android.net.sip.SipSession, int, String);
+ method @Deprecated public void onCallEnded(android.net.sip.SipSession);
+ method @Deprecated public void onCallEstablished(android.net.sip.SipSession, String);
+ method @Deprecated public void onCalling(android.net.sip.SipSession);
+ method @Deprecated public void onError(android.net.sip.SipSession, int, String);
+ method @Deprecated public void onRegistering(android.net.sip.SipSession);
+ method @Deprecated public void onRegistrationDone(android.net.sip.SipSession, int);
+ method @Deprecated public void onRegistrationFailed(android.net.sip.SipSession, int, String);
+ method @Deprecated public void onRegistrationTimeout(android.net.sip.SipSession);
+ method @Deprecated public void onRinging(android.net.sip.SipSession, android.net.sip.SipProfile, String);
+ method @Deprecated public void onRingingBack(android.net.sip.SipSession);
+ }
+
+ @Deprecated public static class SipSession.State {
+ method @Deprecated public static String toString(int);
+ field @Deprecated public static final int DEREGISTERING = 2; // 0x2
+ field @Deprecated public static final int INCOMING_CALL = 3; // 0x3
+ field @Deprecated public static final int INCOMING_CALL_ANSWERING = 4; // 0x4
+ field @Deprecated public static final int IN_CALL = 8; // 0x8
+ field @Deprecated public static final int NOT_DEFINED = 101; // 0x65
+ field @Deprecated public static final int OUTGOING_CALL = 5; // 0x5
+ field @Deprecated public static final int OUTGOING_CALL_CANCELING = 7; // 0x7
+ field @Deprecated public static final int OUTGOING_CALL_RING_BACK = 6; // 0x6
+ field @Deprecated public static final int PINGING = 9; // 0x9
+ field @Deprecated public static final int READY_TO_CALL = 0; // 0x0
+ field @Deprecated public static final int REGISTERING = 1; // 0x1
}
}
@@ -30430,6 +30438,7 @@ package android.os {
field public static final String BASE_OS;
field public static final String CODENAME;
field public static final String INCREMENTAL;
+ field public static final int MEDIA_PERFORMANCE_CLASS;
field public static final int PREVIEW_SDK_INT;
field public static final String RELEASE;
field @NonNull public static final String RELEASE_OR_CODENAME;
@@ -35651,721 +35660,721 @@ package android.provider {
package android.renderscript {
- public class Allocation extends android.renderscript.BaseObj {
- method public void copy1DRangeFrom(int, int, Object);
- method public void copy1DRangeFrom(int, int, int[]);
- method public void copy1DRangeFrom(int, int, short[]);
- method public void copy1DRangeFrom(int, int, byte[]);
- method public void copy1DRangeFrom(int, int, float[]);
- method public void copy1DRangeFrom(int, int, android.renderscript.Allocation, int);
- method public void copy1DRangeFromUnchecked(int, int, Object);
- method public void copy1DRangeFromUnchecked(int, int, int[]);
- method public void copy1DRangeFromUnchecked(int, int, short[]);
- method public void copy1DRangeFromUnchecked(int, int, byte[]);
- method public void copy1DRangeFromUnchecked(int, int, float[]);
- method public void copy1DRangeTo(int, int, Object);
- method public void copy1DRangeTo(int, int, int[]);
- method public void copy1DRangeTo(int, int, short[]);
- method public void copy1DRangeTo(int, int, byte[]);
- method public void copy1DRangeTo(int, int, float[]);
- method public void copy1DRangeToUnchecked(int, int, Object);
- method public void copy1DRangeToUnchecked(int, int, int[]);
- method public void copy1DRangeToUnchecked(int, int, short[]);
- method public void copy1DRangeToUnchecked(int, int, byte[]);
- method public void copy1DRangeToUnchecked(int, int, float[]);
- method public void copy2DRangeFrom(int, int, int, int, Object);
- method public void copy2DRangeFrom(int, int, int, int, byte[]);
- method public void copy2DRangeFrom(int, int, int, int, short[]);
- method public void copy2DRangeFrom(int, int, int, int, int[]);
- method public void copy2DRangeFrom(int, int, int, int, float[]);
- method public void copy2DRangeFrom(int, int, int, int, android.renderscript.Allocation, int, int);
- method public void copy2DRangeFrom(int, int, android.graphics.Bitmap);
- method public void copy2DRangeTo(int, int, int, int, Object);
- method public void copy2DRangeTo(int, int, int, int, byte[]);
- method public void copy2DRangeTo(int, int, int, int, short[]);
- method public void copy2DRangeTo(int, int, int, int, int[]);
- method public void copy2DRangeTo(int, int, int, int, float[]);
- method public void copy3DRangeFrom(int, int, int, int, int, int, Object);
- method public void copy3DRangeFrom(int, int, int, int, int, int, android.renderscript.Allocation, int, int, int);
- method public void copy3DRangeTo(int, int, int, int, int, int, Object);
- method public void copyFrom(android.renderscript.BaseObj[]);
- method public void copyFrom(Object);
- method public void copyFrom(int[]);
- method public void copyFrom(short[]);
- method public void copyFrom(byte[]);
- method public void copyFrom(float[]);
- method public void copyFrom(android.graphics.Bitmap);
- method public void copyFrom(android.renderscript.Allocation);
- method public void copyFromUnchecked(Object);
- method public void copyFromUnchecked(int[]);
- method public void copyFromUnchecked(short[]);
- method public void copyFromUnchecked(byte[]);
- method public void copyFromUnchecked(float[]);
- method public void copyTo(android.graphics.Bitmap);
- method public void copyTo(Object);
- method public void copyTo(byte[]);
- method public void copyTo(short[]);
- method public void copyTo(int[]);
- method public void copyTo(float[]);
- method public static android.renderscript.Allocation[] createAllocations(android.renderscript.RenderScript, android.renderscript.Type, int, int);
- method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
- method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
- method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
- method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap);
- method public static android.renderscript.Allocation createFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
- method public static android.renderscript.Allocation createFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
- method public static android.renderscript.Allocation createFromBitmapResource(android.renderscript.RenderScript, android.content.res.Resources, int, android.renderscript.Allocation.MipmapControl, int);
- method public static android.renderscript.Allocation createFromBitmapResource(android.renderscript.RenderScript, android.content.res.Resources, int);
- method public static android.renderscript.Allocation createFromString(android.renderscript.RenderScript, String, int);
- method public static android.renderscript.Allocation createSized(android.renderscript.RenderScript, android.renderscript.Element, int, int);
- method public static android.renderscript.Allocation createSized(android.renderscript.RenderScript, android.renderscript.Element, int);
- method public static android.renderscript.Allocation createTyped(android.renderscript.RenderScript, android.renderscript.Type, android.renderscript.Allocation.MipmapControl, int);
- method public static android.renderscript.Allocation createTyped(android.renderscript.RenderScript, android.renderscript.Type, int);
- method public static android.renderscript.Allocation createTyped(android.renderscript.RenderScript, android.renderscript.Type);
- method public void generateMipmaps();
- method public java.nio.ByteBuffer getByteBuffer();
- method public int getBytesSize();
- method public android.renderscript.Element getElement();
- method public long getStride();
- method public android.view.Surface getSurface();
- method public long getTimeStamp();
- method public android.renderscript.Type getType();
- method public int getUsage();
- method public void ioReceive();
- method public void ioSend();
+ @Deprecated public class Allocation extends android.renderscript.BaseObj {
+ method @Deprecated public void copy1DRangeFrom(int, int, Object);
+ method @Deprecated public void copy1DRangeFrom(int, int, int[]);
+ method @Deprecated public void copy1DRangeFrom(int, int, short[]);
+ method @Deprecated public void copy1DRangeFrom(int, int, byte[]);
+ method @Deprecated public void copy1DRangeFrom(int, int, float[]);
+ method @Deprecated public void copy1DRangeFrom(int, int, android.renderscript.Allocation, int);
+ method @Deprecated public void copy1DRangeFromUnchecked(int, int, Object);
+ method @Deprecated public void copy1DRangeFromUnchecked(int, int, int[]);
+ method @Deprecated public void copy1DRangeFromUnchecked(int, int, short[]);
+ method @Deprecated public void copy1DRangeFromUnchecked(int, int, byte[]);
+ method @Deprecated public void copy1DRangeFromUnchecked(int, int, float[]);
+ method @Deprecated public void copy1DRangeTo(int, int, Object);
+ method @Deprecated public void copy1DRangeTo(int, int, int[]);
+ method @Deprecated public void copy1DRangeTo(int, int, short[]);
+ method @Deprecated public void copy1DRangeTo(int, int, byte[]);
+ method @Deprecated public void copy1DRangeTo(int, int, float[]);
+ method @Deprecated public void copy1DRangeToUnchecked(int, int, Object);
+ method @Deprecated public void copy1DRangeToUnchecked(int, int, int[]);
+ method @Deprecated public void copy1DRangeToUnchecked(int, int, short[]);
+ method @Deprecated public void copy1DRangeToUnchecked(int, int, byte[]);
+ method @Deprecated public void copy1DRangeToUnchecked(int, int, float[]);
+ method @Deprecated public void copy2DRangeFrom(int, int, int, int, Object);
+ method @Deprecated public void copy2DRangeFrom(int, int, int, int, byte[]);
+ method @Deprecated public void copy2DRangeFrom(int, int, int, int, short[]);
+ method @Deprecated public void copy2DRangeFrom(int, int, int, int, int[]);
+ method @Deprecated public void copy2DRangeFrom(int, int, int, int, float[]);
+ method @Deprecated public void copy2DRangeFrom(int, int, int, int, android.renderscript.Allocation, int, int);
+ method @Deprecated public void copy2DRangeFrom(int, int, android.graphics.Bitmap);
+ method @Deprecated public void copy2DRangeTo(int, int, int, int, Object);
+ method @Deprecated public void copy2DRangeTo(int, int, int, int, byte[]);
+ method @Deprecated public void copy2DRangeTo(int, int, int, int, short[]);
+ method @Deprecated public void copy2DRangeTo(int, int, int, int, int[]);
+ method @Deprecated public void copy2DRangeTo(int, int, int, int, float[]);
+ method @Deprecated public void copy3DRangeFrom(int, int, int, int, int, int, Object);
+ method @Deprecated public void copy3DRangeFrom(int, int, int, int, int, int, android.renderscript.Allocation, int, int, int);
+ method @Deprecated public void copy3DRangeTo(int, int, int, int, int, int, Object);
+ method @Deprecated public void copyFrom(android.renderscript.BaseObj[]);
+ method @Deprecated public void copyFrom(Object);
+ method @Deprecated public void copyFrom(int[]);
+ method @Deprecated public void copyFrom(short[]);
+ method @Deprecated public void copyFrom(byte[]);
+ method @Deprecated public void copyFrom(float[]);
+ method @Deprecated public void copyFrom(android.graphics.Bitmap);
+ method @Deprecated public void copyFrom(android.renderscript.Allocation);
+ method @Deprecated public void copyFromUnchecked(Object);
+ method @Deprecated public void copyFromUnchecked(int[]);
+ method @Deprecated public void copyFromUnchecked(short[]);
+ method @Deprecated public void copyFromUnchecked(byte[]);
+ method @Deprecated public void copyFromUnchecked(float[]);
+ method @Deprecated public void copyTo(android.graphics.Bitmap);
+ method @Deprecated public void copyTo(Object);
+ method @Deprecated public void copyTo(byte[]);
+ method @Deprecated public void copyTo(short[]);
+ method @Deprecated public void copyTo(int[]);
+ method @Deprecated public void copyTo(float[]);
+ method @Deprecated public static android.renderscript.Allocation[] createAllocations(android.renderscript.RenderScript, android.renderscript.Type, int, int);
+ method @Deprecated public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
+ method @Deprecated public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
+ method @Deprecated public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
+ method @Deprecated public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap);
+ method @Deprecated public static android.renderscript.Allocation createFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
+ method @Deprecated public static android.renderscript.Allocation createFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
+ method @Deprecated public static android.renderscript.Allocation createFromBitmapResource(android.renderscript.RenderScript, android.content.res.Resources, int, android.renderscript.Allocation.MipmapControl, int);
+ method @Deprecated public static android.renderscript.Allocation createFromBitmapResource(android.renderscript.RenderScript, android.content.res.Resources, int);
+ method @Deprecated public static android.renderscript.Allocation createFromString(android.renderscript.RenderScript, String, int);
+ method @Deprecated public static android.renderscript.Allocation createSized(android.renderscript.RenderScript, android.renderscript.Element, int, int);
+ method @Deprecated public static android.renderscript.Allocation createSized(android.renderscript.RenderScript, android.renderscript.Element, int);
+ method @Deprecated public static android.renderscript.Allocation createTyped(android.renderscript.RenderScript, android.renderscript.Type, android.renderscript.Allocation.MipmapControl, int);
+ method @Deprecated public static android.renderscript.Allocation createTyped(android.renderscript.RenderScript, android.renderscript.Type, int);
+ method @Deprecated public static android.renderscript.Allocation createTyped(android.renderscript.RenderScript, android.renderscript.Type);
+ method @Deprecated public void generateMipmaps();
+ method @Deprecated public java.nio.ByteBuffer getByteBuffer();
+ method @Deprecated public int getBytesSize();
+ method @Deprecated public android.renderscript.Element getElement();
+ method @Deprecated public long getStride();
+ method @Deprecated public android.view.Surface getSurface();
+ method @Deprecated public long getTimeStamp();
+ method @Deprecated public android.renderscript.Type getType();
+ method @Deprecated public int getUsage();
+ method @Deprecated public void ioReceive();
+ method @Deprecated public void ioSend();
method @Deprecated public void resize(int);
- method public void setAutoPadding(boolean);
- method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
- method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
- method public void setFromFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
- method public void setOnBufferAvailableListener(android.renderscript.Allocation.OnBufferAvailableListener);
- method public void setSurface(android.view.Surface);
- method public void syncAll(int);
- field public static final int USAGE_GRAPHICS_CONSTANTS = 8; // 0x8
- field public static final int USAGE_GRAPHICS_RENDER_TARGET = 16; // 0x10
- field public static final int USAGE_GRAPHICS_TEXTURE = 2; // 0x2
- field public static final int USAGE_GRAPHICS_VERTEX = 4; // 0x4
- field public static final int USAGE_IO_INPUT = 32; // 0x20
- field public static final int USAGE_IO_OUTPUT = 64; // 0x40
- field public static final int USAGE_SCRIPT = 1; // 0x1
- field public static final int USAGE_SHARED = 128; // 0x80
- }
-
- public enum Allocation.MipmapControl {
- enum_constant public static final android.renderscript.Allocation.MipmapControl MIPMAP_FULL;
- enum_constant public static final android.renderscript.Allocation.MipmapControl MIPMAP_NONE;
- enum_constant public static final android.renderscript.Allocation.MipmapControl MIPMAP_ON_SYNC_TO_TEXTURE;
- }
-
- public static interface Allocation.OnBufferAvailableListener {
- method public void onBufferAvailable(android.renderscript.Allocation);
- }
-
- public class AllocationAdapter extends android.renderscript.Allocation {
- method public static android.renderscript.AllocationAdapter create1D(android.renderscript.RenderScript, android.renderscript.Allocation);
- method public static android.renderscript.AllocationAdapter create2D(android.renderscript.RenderScript, android.renderscript.Allocation);
- method public static android.renderscript.AllocationAdapter createTyped(android.renderscript.RenderScript, android.renderscript.Allocation, android.renderscript.Type);
- method public void resize(int);
- method public void setFace(android.renderscript.Type.CubemapFace);
- method public void setLOD(int);
- method public void setX(int);
- method public void setY(int);
- method public void setZ(int);
- }
-
- public class BaseObj {
- method public void destroy();
- method public String getName();
- method public void setName(String);
+ method @Deprecated public void setAutoPadding(boolean);
+ method @Deprecated public void setFromFieldPacker(int, android.renderscript.FieldPacker);
+ method @Deprecated public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
+ method @Deprecated public void setFromFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
+ method @Deprecated public void setOnBufferAvailableListener(android.renderscript.Allocation.OnBufferAvailableListener);
+ method @Deprecated public void setSurface(android.view.Surface);
+ method @Deprecated public void syncAll(int);
+ field @Deprecated public static final int USAGE_GRAPHICS_CONSTANTS = 8; // 0x8
+ field @Deprecated public static final int USAGE_GRAPHICS_RENDER_TARGET = 16; // 0x10
+ field @Deprecated public static final int USAGE_GRAPHICS_TEXTURE = 2; // 0x2
+ field @Deprecated public static final int USAGE_GRAPHICS_VERTEX = 4; // 0x4
+ field @Deprecated public static final int USAGE_IO_INPUT = 32; // 0x20
+ field @Deprecated public static final int USAGE_IO_OUTPUT = 64; // 0x40
+ field @Deprecated public static final int USAGE_SCRIPT = 1; // 0x1
+ field @Deprecated public static final int USAGE_SHARED = 128; // 0x80
+ }
+
+ @Deprecated public enum Allocation.MipmapControl {
+ enum_constant @Deprecated public static final android.renderscript.Allocation.MipmapControl MIPMAP_FULL;
+ enum_constant @Deprecated public static final android.renderscript.Allocation.MipmapControl MIPMAP_NONE;
+ enum_constant @Deprecated public static final android.renderscript.Allocation.MipmapControl MIPMAP_ON_SYNC_TO_TEXTURE;
+ }
+
+ @Deprecated public static interface Allocation.OnBufferAvailableListener {
+ method @Deprecated public void onBufferAvailable(android.renderscript.Allocation);
+ }
+
+ @Deprecated public class AllocationAdapter extends android.renderscript.Allocation {
+ method @Deprecated public static android.renderscript.AllocationAdapter create1D(android.renderscript.RenderScript, android.renderscript.Allocation);
+ method @Deprecated public static android.renderscript.AllocationAdapter create2D(android.renderscript.RenderScript, android.renderscript.Allocation);
+ method @Deprecated public static android.renderscript.AllocationAdapter createTyped(android.renderscript.RenderScript, android.renderscript.Allocation, android.renderscript.Type);
+ method @Deprecated public void resize(int);
+ method @Deprecated public void setFace(android.renderscript.Type.CubemapFace);
+ method @Deprecated public void setLOD(int);
+ method @Deprecated public void setX(int);
+ method @Deprecated public void setY(int);
+ method @Deprecated public void setZ(int);
}
- public class Byte2 {
- ctor public Byte2();
- ctor public Byte2(byte, byte);
- field public byte x;
- field public byte y;
- }
-
- public class Byte3 {
- ctor public Byte3();
- ctor public Byte3(byte, byte, byte);
- field public byte x;
- field public byte y;
- field public byte z;
- }
-
- public class Byte4 {
- ctor public Byte4();
- ctor public Byte4(byte, byte, byte, byte);
- field public byte w;
- field public byte x;
- field public byte y;
- field public byte z;
- }
-
- public class Double2 {
- ctor public Double2();
- ctor public Double2(double, double);
- field public double x;
- field public double y;
- }
-
- public class Double3 {
- ctor public Double3();
- ctor public Double3(double, double, double);
- field public double x;
- field public double y;
- field public double z;
- }
-
- public class Double4 {
- ctor public Double4();
- ctor public Double4(double, double, double, double);
- field public double w;
- field public double x;
- field public double y;
- field public double z;
- }
-
- public class Element extends android.renderscript.BaseObj {
- method public static android.renderscript.Element ALLOCATION(android.renderscript.RenderScript);
- method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
- method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
- method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
- method public static android.renderscript.Element F16(android.renderscript.RenderScript);
- method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element F32(android.renderscript.RenderScript);
- method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element F32_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element F64(android.renderscript.RenderScript);
- method public static android.renderscript.Element F64_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element F64_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element F64_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element FONT(android.renderscript.RenderScript);
- method public static android.renderscript.Element I16(android.renderscript.RenderScript);
- method public static android.renderscript.Element I16_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element I16_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element I16_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element I32(android.renderscript.RenderScript);
- method public static android.renderscript.Element I32_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element I32_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element I32_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element I64(android.renderscript.RenderScript);
- method public static android.renderscript.Element I64_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element I64_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element I64_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element I8(android.renderscript.RenderScript);
- method public static android.renderscript.Element I8_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element I8_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element I8_4(android.renderscript.RenderScript);
+ @Deprecated public class BaseObj {
+ method @Deprecated public void destroy();
+ method @Deprecated public String getName();
+ method @Deprecated public void setName(String);
+ }
+
+ @Deprecated public class Byte2 {
+ ctor @Deprecated public Byte2();
+ ctor @Deprecated public Byte2(byte, byte);
+ field @Deprecated public byte x;
+ field @Deprecated public byte y;
+ }
+
+ @Deprecated public class Byte3 {
+ ctor @Deprecated public Byte3();
+ ctor @Deprecated public Byte3(byte, byte, byte);
+ field @Deprecated public byte x;
+ field @Deprecated public byte y;
+ field @Deprecated public byte z;
+ }
+
+ @Deprecated public class Byte4 {
+ ctor @Deprecated public Byte4();
+ ctor @Deprecated public Byte4(byte, byte, byte, byte);
+ field @Deprecated public byte w;
+ field @Deprecated public byte x;
+ field @Deprecated public byte y;
+ field @Deprecated public byte z;
+ }
+
+ @Deprecated public class Double2 {
+ ctor @Deprecated public Double2();
+ ctor @Deprecated public Double2(double, double);
+ field @Deprecated public double x;
+ field @Deprecated public double y;
+ }
+
+ @Deprecated public class Double3 {
+ ctor @Deprecated public Double3();
+ ctor @Deprecated public Double3(double, double, double);
+ field @Deprecated public double x;
+ field @Deprecated public double y;
+ field @Deprecated public double z;
+ }
+
+ @Deprecated public class Double4 {
+ ctor @Deprecated public Double4();
+ ctor @Deprecated public Double4(double, double, double, double);
+ field @Deprecated public double w;
+ field @Deprecated public double x;
+ field @Deprecated public double y;
+ field @Deprecated public double z;
+ }
+
+ @Deprecated public class Element extends android.renderscript.BaseObj {
+ method @Deprecated public static android.renderscript.Element ALLOCATION(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element A_8(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F16(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F32(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F32_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F64(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F64_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F64_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element F64_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element FONT(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I16(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I16_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I16_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I16_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I32(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I32_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I32_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I32_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I64(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I64_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I64_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I64_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I8(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I8_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I8_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element I8_4(android.renderscript.RenderScript);
method @Deprecated public static android.renderscript.Element MATRIX4X4(android.renderscript.RenderScript);
- method public static android.renderscript.Element MATRIX_2X2(android.renderscript.RenderScript);
- method public static android.renderscript.Element MATRIX_3X3(android.renderscript.RenderScript);
- method public static android.renderscript.Element MATRIX_4X4(android.renderscript.RenderScript);
- method public static android.renderscript.Element MESH(android.renderscript.RenderScript);
- method public static android.renderscript.Element PROGRAM_FRAGMENT(android.renderscript.RenderScript);
- method public static android.renderscript.Element PROGRAM_RASTER(android.renderscript.RenderScript);
- method public static android.renderscript.Element PROGRAM_STORE(android.renderscript.RenderScript);
- method public static android.renderscript.Element PROGRAM_VERTEX(android.renderscript.RenderScript);
- method public static android.renderscript.Element RGBA_4444(android.renderscript.RenderScript);
- method public static android.renderscript.Element RGBA_5551(android.renderscript.RenderScript);
- method public static android.renderscript.Element RGBA_8888(android.renderscript.RenderScript);
- method public static android.renderscript.Element RGB_565(android.renderscript.RenderScript);
- method public static android.renderscript.Element RGB_888(android.renderscript.RenderScript);
- method public static android.renderscript.Element SAMPLER(android.renderscript.RenderScript);
- method public static android.renderscript.Element SCRIPT(android.renderscript.RenderScript);
- method public static android.renderscript.Element TYPE(android.renderscript.RenderScript);
- method public static android.renderscript.Element U16(android.renderscript.RenderScript);
- method public static android.renderscript.Element U16_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element U16_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element U16_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element U32(android.renderscript.RenderScript);
- method public static android.renderscript.Element U32_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element U32_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element U32_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element U64(android.renderscript.RenderScript);
- method public static android.renderscript.Element U64_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element U64_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element U64_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element U8(android.renderscript.RenderScript);
- method public static android.renderscript.Element U8_2(android.renderscript.RenderScript);
- method public static android.renderscript.Element U8_3(android.renderscript.RenderScript);
- method public static android.renderscript.Element U8_4(android.renderscript.RenderScript);
- method public static android.renderscript.Element YUV(android.renderscript.RenderScript);
- method public static android.renderscript.Element createPixel(android.renderscript.RenderScript, android.renderscript.Element.DataType, android.renderscript.Element.DataKind);
- method public static android.renderscript.Element createVector(android.renderscript.RenderScript, android.renderscript.Element.DataType, int);
- method public int getBytesSize();
- method public android.renderscript.Element.DataKind getDataKind();
- method public android.renderscript.Element.DataType getDataType();
- method public android.renderscript.Element getSubElement(int);
- method public int getSubElementArraySize(int);
- method public int getSubElementCount();
- method public String getSubElementName(int);
- method public int getSubElementOffsetBytes(int);
- method public int getVectorSize();
- method public boolean isCompatible(android.renderscript.Element);
- method public boolean isComplex();
- }
-
- public static class Element.Builder {
- ctor public Element.Builder(android.renderscript.RenderScript);
- method public android.renderscript.Element.Builder add(android.renderscript.Element, String, int);
- method public android.renderscript.Element.Builder add(android.renderscript.Element, String);
- method public android.renderscript.Element create();
- }
-
- public enum Element.DataKind {
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_A;
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_DEPTH;
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_L;
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_LA;
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_RGB;
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_RGBA;
- enum_constant public static final android.renderscript.Element.DataKind PIXEL_YUV;
- enum_constant public static final android.renderscript.Element.DataKind USER;
- }
-
- public enum Element.DataType {
- enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
- enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
- enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
- enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
- enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
- enum_constant public static final android.renderscript.Element.DataType MATRIX_3X3;
- enum_constant public static final android.renderscript.Element.DataType MATRIX_4X4;
- enum_constant public static final android.renderscript.Element.DataType NONE;
- enum_constant public static final android.renderscript.Element.DataType RS_ALLOCATION;
- enum_constant public static final android.renderscript.Element.DataType RS_ELEMENT;
- enum_constant public static final android.renderscript.Element.DataType RS_FONT;
- enum_constant public static final android.renderscript.Element.DataType RS_MESH;
- enum_constant public static final android.renderscript.Element.DataType RS_PROGRAM_FRAGMENT;
- enum_constant public static final android.renderscript.Element.DataType RS_PROGRAM_RASTER;
- enum_constant public static final android.renderscript.Element.DataType RS_PROGRAM_STORE;
- enum_constant public static final android.renderscript.Element.DataType RS_PROGRAM_VERTEX;
- enum_constant public static final android.renderscript.Element.DataType RS_SAMPLER;
- enum_constant public static final android.renderscript.Element.DataType RS_SCRIPT;
- enum_constant public static final android.renderscript.Element.DataType RS_TYPE;
- enum_constant public static final android.renderscript.Element.DataType SIGNED_16;
- enum_constant public static final android.renderscript.Element.DataType SIGNED_32;
- enum_constant public static final android.renderscript.Element.DataType SIGNED_64;
- enum_constant public static final android.renderscript.Element.DataType SIGNED_8;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_16;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_32;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_4_4_4_4;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_5_5_5_1;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_5_6_5;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_64;
- enum_constant public static final android.renderscript.Element.DataType UNSIGNED_8;
- }
-
- public class FieldPacker {
- ctor public FieldPacker(int);
- ctor public FieldPacker(byte[]);
- method public void addBoolean(boolean);
- method public void addF32(float);
- method public void addF32(android.renderscript.Float2);
- method public void addF32(android.renderscript.Float3);
- method public void addF32(android.renderscript.Float4);
- method public void addF64(double);
- method public void addF64(android.renderscript.Double2);
- method public void addF64(android.renderscript.Double3);
- method public void addF64(android.renderscript.Double4);
- method public void addI16(short);
- method public void addI16(android.renderscript.Short2);
- method public void addI16(android.renderscript.Short3);
- method public void addI16(android.renderscript.Short4);
- method public void addI32(int);
- method public void addI32(android.renderscript.Int2);
- method public void addI32(android.renderscript.Int3);
- method public void addI32(android.renderscript.Int4);
- method public void addI64(long);
- method public void addI64(android.renderscript.Long2);
- method public void addI64(android.renderscript.Long3);
- method public void addI64(android.renderscript.Long4);
- method public void addI8(byte);
- method public void addI8(android.renderscript.Byte2);
- method public void addI8(android.renderscript.Byte3);
- method public void addI8(android.renderscript.Byte4);
- method public void addMatrix(android.renderscript.Matrix4f);
- method public void addMatrix(android.renderscript.Matrix3f);
- method public void addMatrix(android.renderscript.Matrix2f);
- method public void addObj(android.renderscript.BaseObj);
- method public void addU16(int);
- method public void addU16(android.renderscript.Int2);
- method public void addU16(android.renderscript.Int3);
- method public void addU16(android.renderscript.Int4);
- method public void addU32(long);
- method public void addU32(android.renderscript.Long2);
- method public void addU32(android.renderscript.Long3);
- method public void addU32(android.renderscript.Long4);
- method public void addU64(long);
- method public void addU64(android.renderscript.Long2);
- method public void addU64(android.renderscript.Long3);
- method public void addU64(android.renderscript.Long4);
- method public void addU8(short);
- method public void addU8(android.renderscript.Short2);
- method public void addU8(android.renderscript.Short3);
- method public void addU8(android.renderscript.Short4);
- method public void align(int);
- method public final byte[] getData();
- method public void reset();
- method public void reset(int);
- method public void skip(int);
- method public boolean subBoolean();
- method public android.renderscript.Byte2 subByte2();
- method public android.renderscript.Byte3 subByte3();
- method public android.renderscript.Byte4 subByte4();
- method public android.renderscript.Double2 subDouble2();
- method public android.renderscript.Double3 subDouble3();
- method public android.renderscript.Double4 subDouble4();
- method public float subF32();
- method public double subF64();
- method public android.renderscript.Float2 subFloat2();
- method public android.renderscript.Float3 subFloat3();
- method public android.renderscript.Float4 subFloat4();
- method public short subI16();
- method public int subI32();
- method public long subI64();
- method public byte subI8();
- method public android.renderscript.Int2 subInt2();
- method public android.renderscript.Int3 subInt3();
- method public android.renderscript.Int4 subInt4();
- method public android.renderscript.Long2 subLong2();
- method public android.renderscript.Long3 subLong3();
- method public android.renderscript.Long4 subLong4();
- method public android.renderscript.Matrix2f subMatrix2f();
- method public android.renderscript.Matrix3f subMatrix3f();
- method public android.renderscript.Matrix4f subMatrix4f();
- method public android.renderscript.Short2 subShort2();
- method public android.renderscript.Short3 subShort3();
- method public android.renderscript.Short4 subShort4();
- method public void subalign(int);
- }
-
- public class Float2 {
- ctor public Float2();
- ctor public Float2(float, float);
- field public float x;
- field public float y;
- }
-
- public class Float3 {
- ctor public Float3();
- ctor public Float3(float, float, float);
- field public float x;
- field public float y;
- field public float z;
- }
-
- public class Float4 {
- ctor public Float4();
- ctor public Float4(float, float, float, float);
- field public float w;
- field public float x;
- field public float y;
- field public float z;
- }
-
- public class Int2 {
- ctor public Int2();
- ctor public Int2(int, int);
- field public int x;
- field public int y;
- }
-
- public class Int3 {
- ctor public Int3();
- ctor public Int3(int, int, int);
- field public int x;
- field public int y;
- field public int z;
- }
-
- public class Int4 {
- ctor public Int4();
- ctor public Int4(int, int, int, int);
- field public int w;
- field public int x;
- field public int y;
- field public int z;
- }
-
- public class Long2 {
- ctor public Long2();
- ctor public Long2(long, long);
- field public long x;
- field public long y;
- }
-
- public class Long3 {
- ctor public Long3();
- ctor public Long3(long, long, long);
- field public long x;
- field public long y;
- field public long z;
- }
-
- public class Long4 {
- ctor public Long4();
- ctor public Long4(long, long, long, long);
- field public long w;
- field public long x;
- field public long y;
- field public long z;
- }
-
- public class Matrix2f {
- ctor public Matrix2f();
- ctor public Matrix2f(float[]);
- method public float get(int, int);
- method public float[] getArray();
- method public void load(android.renderscript.Matrix2f);
- method public void loadIdentity();
- method public void loadMultiply(android.renderscript.Matrix2f, android.renderscript.Matrix2f);
- method public void loadRotate(float);
- method public void loadScale(float, float);
- method public void multiply(android.renderscript.Matrix2f);
- method public void rotate(float);
- method public void scale(float, float);
- method public void set(int, int, float);
- method public void transpose();
- }
-
- public class Matrix3f {
- ctor public Matrix3f();
- ctor public Matrix3f(float[]);
- method public float get(int, int);
- method public float[] getArray();
- method public void load(android.renderscript.Matrix3f);
- method public void loadIdentity();
- method public void loadMultiply(android.renderscript.Matrix3f, android.renderscript.Matrix3f);
- method public void loadRotate(float, float, float, float);
- method public void loadRotate(float);
- method public void loadScale(float, float);
- method public void loadScale(float, float, float);
- method public void loadTranslate(float, float);
- method public void multiply(android.renderscript.Matrix3f);
- method public void rotate(float, float, float, float);
- method public void rotate(float);
- method public void scale(float, float);
- method public void scale(float, float, float);
- method public void set(int, int, float);
- method public void translate(float, float);
- method public void transpose();
- }
-
- public class Matrix4f {
- ctor public Matrix4f();
- ctor public Matrix4f(float[]);
- method public float get(int, int);
- method public float[] getArray();
- method public boolean inverse();
- method public boolean inverseTranspose();
- method public void load(android.renderscript.Matrix4f);
- method public void loadFrustum(float, float, float, float, float, float);
- method public void loadIdentity();
- method public void loadMultiply(android.renderscript.Matrix4f, android.renderscript.Matrix4f);
- method public void loadOrtho(float, float, float, float, float, float);
- method public void loadOrthoWindow(int, int);
- method public void loadPerspective(float, float, float, float);
- method public void loadProjectionNormalized(int, int);
- method public void loadRotate(float, float, float, float);
- method public void loadScale(float, float, float);
- method public void loadTranslate(float, float, float);
- method public void multiply(android.renderscript.Matrix4f);
- method public void rotate(float, float, float, float);
- method public void scale(float, float, float);
- method public void set(int, int, float);
- method public void translate(float, float, float);
- method public void transpose();
- }
-
- public class RSDriverException extends android.renderscript.RSRuntimeException {
- ctor public RSDriverException(String);
- }
-
- public class RSIllegalArgumentException extends android.renderscript.RSRuntimeException {
- ctor public RSIllegalArgumentException(String);
- }
-
- public class RSInvalidStateException extends android.renderscript.RSRuntimeException {
- ctor public RSInvalidStateException(String);
- }
-
- public class RSRuntimeException extends java.lang.RuntimeException {
- ctor public RSRuntimeException(String);
- }
-
- public class RenderScript {
- method public void contextDump();
- method public static android.renderscript.RenderScript create(android.content.Context);
- method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
- method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
- method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
- method public void destroy();
- method public void finish();
- method public final android.content.Context getApplicationContext();
- method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
- method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
- method public static long getMinorVersion();
- method public static void releaseAllContexts();
- method public void sendMessage(int, int[]);
- method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
- method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
- method public void setPriority(android.renderscript.RenderScript.Priority);
- field public static final int CREATE_FLAG_LOW_LATENCY = 2; // 0x2
- field public static final int CREATE_FLAG_LOW_POWER = 4; // 0x4
- field public static final int CREATE_FLAG_NONE = 0; // 0x0
- }
-
- public enum RenderScript.ContextType {
- enum_constant public static final android.renderscript.RenderScript.ContextType DEBUG;
- enum_constant public static final android.renderscript.RenderScript.ContextType NORMAL;
- enum_constant public static final android.renderscript.RenderScript.ContextType PROFILE;
- }
-
- public enum RenderScript.Priority {
- enum_constant public static final android.renderscript.RenderScript.Priority LOW;
- enum_constant public static final android.renderscript.RenderScript.Priority NORMAL;
- }
-
- public static class RenderScript.RSErrorHandler implements java.lang.Runnable {
- ctor public RenderScript.RSErrorHandler();
- method public void run();
- field protected String mErrorMessage;
- field protected int mErrorNum;
- }
-
- public static class RenderScript.RSMessageHandler implements java.lang.Runnable {
- ctor public RenderScript.RSMessageHandler();
- method public void run();
- field protected int[] mData;
- field protected int mID;
- field protected int mLength;
- }
-
- public class Sampler extends android.renderscript.BaseObj {
- method public static android.renderscript.Sampler CLAMP_LINEAR(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler CLAMP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler CLAMP_NEAREST(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler MIRRORED_REPEAT_LINEAR(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler MIRRORED_REPEAT_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler MIRRORED_REPEAT_NEAREST(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler WRAP_LINEAR(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler WRAP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
- method public static android.renderscript.Sampler WRAP_NEAREST(android.renderscript.RenderScript);
- method public float getAnisotropy();
- method public android.renderscript.Sampler.Value getMagnification();
- method public android.renderscript.Sampler.Value getMinification();
- method public android.renderscript.Sampler.Value getWrapS();
- method public android.renderscript.Sampler.Value getWrapT();
- }
-
- public static class Sampler.Builder {
- ctor public Sampler.Builder(android.renderscript.RenderScript);
- method public android.renderscript.Sampler create();
- method public void setAnisotropy(float);
- method public void setMagnification(android.renderscript.Sampler.Value);
- method public void setMinification(android.renderscript.Sampler.Value);
- method public void setWrapS(android.renderscript.Sampler.Value);
- method public void setWrapT(android.renderscript.Sampler.Value);
- }
-
- public enum Sampler.Value {
- enum_constant public static final android.renderscript.Sampler.Value CLAMP;
- enum_constant public static final android.renderscript.Sampler.Value LINEAR;
- enum_constant public static final android.renderscript.Sampler.Value LINEAR_MIP_LINEAR;
- enum_constant public static final android.renderscript.Sampler.Value LINEAR_MIP_NEAREST;
- enum_constant public static final android.renderscript.Sampler.Value MIRRORED_REPEAT;
- enum_constant public static final android.renderscript.Sampler.Value NEAREST;
- enum_constant public static final android.renderscript.Sampler.Value WRAP;
- }
-
- public class Script extends android.renderscript.BaseObj {
- method public void bindAllocation(android.renderscript.Allocation, int);
- method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
- method protected android.renderscript.Script.InvokeID createInvokeID(int);
- method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
- method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
- method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
- method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
- method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
- method public boolean getVarB(int);
- method public double getVarD(int);
- method public float getVarF(int);
- method public int getVarI(int);
- method public long getVarJ(int);
- method public void getVarV(int, android.renderscript.FieldPacker);
- method protected void invoke(int);
- method protected void invoke(int, android.renderscript.FieldPacker);
- method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void setTimeZone(String);
- method public void setVar(int, float);
- method public void setVar(int, double);
- method public void setVar(int, int);
- method public void setVar(int, long);
- method public void setVar(int, boolean);
- method public void setVar(int, android.renderscript.BaseObj);
- method public void setVar(int, android.renderscript.FieldPacker);
- method public void setVar(int, android.renderscript.FieldPacker, android.renderscript.Element, int[]);
- }
-
- public static class Script.Builder {
- }
-
- public static class Script.FieldBase {
- ctor protected Script.FieldBase();
- method public android.renderscript.Allocation getAllocation();
- method public android.renderscript.Element getElement();
- method public android.renderscript.Type getType();
- method protected void init(android.renderscript.RenderScript, int);
- method protected void init(android.renderscript.RenderScript, int, int);
- method public void updateAllocation();
- field protected android.renderscript.Allocation mAllocation;
- field protected android.renderscript.Element mElement;
- }
-
- public static final class Script.FieldID extends android.renderscript.BaseObj {
- }
-
- public static final class Script.InvokeID extends android.renderscript.BaseObj {
- }
-
- public static final class Script.KernelID extends android.renderscript.BaseObj {
+ method @Deprecated public static android.renderscript.Element MATRIX_2X2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element MATRIX_3X3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element MATRIX_4X4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element MESH(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element PROGRAM_FRAGMENT(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element PROGRAM_RASTER(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element PROGRAM_STORE(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element PROGRAM_VERTEX(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element RGBA_4444(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element RGBA_5551(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element RGBA_8888(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element RGB_565(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element RGB_888(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element SAMPLER(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element SCRIPT(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element TYPE(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U16(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U16_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U16_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U16_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U32(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U32_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U32_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U32_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U64(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U64_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U64_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U64_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U8(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U8_2(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U8_3(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element U8_4(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element YUV(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Element createPixel(android.renderscript.RenderScript, android.renderscript.Element.DataType, android.renderscript.Element.DataKind);
+ method @Deprecated public static android.renderscript.Element createVector(android.renderscript.RenderScript, android.renderscript.Element.DataType, int);
+ method @Deprecated public int getBytesSize();
+ method @Deprecated public android.renderscript.Element.DataKind getDataKind();
+ method @Deprecated public android.renderscript.Element.DataType getDataType();
+ method @Deprecated public android.renderscript.Element getSubElement(int);
+ method @Deprecated public int getSubElementArraySize(int);
+ method @Deprecated public int getSubElementCount();
+ method @Deprecated public String getSubElementName(int);
+ method @Deprecated public int getSubElementOffsetBytes(int);
+ method @Deprecated public int getVectorSize();
+ method @Deprecated public boolean isCompatible(android.renderscript.Element);
+ method @Deprecated public boolean isComplex();
+ }
+
+ @Deprecated public static class Element.Builder {
+ ctor @Deprecated public Element.Builder(android.renderscript.RenderScript);
+ method @Deprecated public android.renderscript.Element.Builder add(android.renderscript.Element, String, int);
+ method @Deprecated public android.renderscript.Element.Builder add(android.renderscript.Element, String);
+ method @Deprecated public android.renderscript.Element create();
+ }
+
+ @Deprecated public enum Element.DataKind {
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_A;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_DEPTH;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_L;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_LA;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_RGB;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_RGBA;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind PIXEL_YUV;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataKind USER;
+ }
+
+ @Deprecated public enum Element.DataType {
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType BOOLEAN;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType FLOAT_16;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType FLOAT_32;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType FLOAT_64;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType MATRIX_2X2;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType MATRIX_3X3;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType MATRIX_4X4;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType NONE;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_ALLOCATION;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_ELEMENT;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_FONT;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_MESH;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_PROGRAM_FRAGMENT;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_PROGRAM_RASTER;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_PROGRAM_STORE;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_PROGRAM_VERTEX;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_SAMPLER;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_SCRIPT;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType RS_TYPE;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType SIGNED_16;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType SIGNED_32;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType SIGNED_64;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType SIGNED_8;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_16;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_32;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_4_4_4_4;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_5_5_5_1;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_5_6_5;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_64;
+ enum_constant @Deprecated public static final android.renderscript.Element.DataType UNSIGNED_8;
+ }
+
+ @Deprecated public class FieldPacker {
+ ctor @Deprecated public FieldPacker(int);
+ ctor @Deprecated public FieldPacker(byte[]);
+ method @Deprecated public void addBoolean(boolean);
+ method @Deprecated public void addF32(float);
+ method @Deprecated public void addF32(android.renderscript.Float2);
+ method @Deprecated public void addF32(android.renderscript.Float3);
+ method @Deprecated public void addF32(android.renderscript.Float4);
+ method @Deprecated public void addF64(double);
+ method @Deprecated public void addF64(android.renderscript.Double2);
+ method @Deprecated public void addF64(android.renderscript.Double3);
+ method @Deprecated public void addF64(android.renderscript.Double4);
+ method @Deprecated public void addI16(short);
+ method @Deprecated public void addI16(android.renderscript.Short2);
+ method @Deprecated public void addI16(android.renderscript.Short3);
+ method @Deprecated public void addI16(android.renderscript.Short4);
+ method @Deprecated public void addI32(int);
+ method @Deprecated public void addI32(android.renderscript.Int2);
+ method @Deprecated public void addI32(android.renderscript.Int3);
+ method @Deprecated public void addI32(android.renderscript.Int4);
+ method @Deprecated public void addI64(long);
+ method @Deprecated public void addI64(android.renderscript.Long2);
+ method @Deprecated public void addI64(android.renderscript.Long3);
+ method @Deprecated public void addI64(android.renderscript.Long4);
+ method @Deprecated public void addI8(byte);
+ method @Deprecated public void addI8(android.renderscript.Byte2);
+ method @Deprecated public void addI8(android.renderscript.Byte3);
+ method @Deprecated public void addI8(android.renderscript.Byte4);
+ method @Deprecated public void addMatrix(android.renderscript.Matrix4f);
+ method @Deprecated public void addMatrix(android.renderscript.Matrix3f);
+ method @Deprecated public void addMatrix(android.renderscript.Matrix2f);
+ method @Deprecated public void addObj(android.renderscript.BaseObj);
+ method @Deprecated public void addU16(int);
+ method @Deprecated public void addU16(android.renderscript.Int2);
+ method @Deprecated public void addU16(android.renderscript.Int3);
+ method @Deprecated public void addU16(android.renderscript.Int4);
+ method @Deprecated public void addU32(long);
+ method @Deprecated public void addU32(android.renderscript.Long2);
+ method @Deprecated public void addU32(android.renderscript.Long3);
+ method @Deprecated public void addU32(android.renderscript.Long4);
+ method @Deprecated public void addU64(long);
+ method @Deprecated public void addU64(android.renderscript.Long2);
+ method @Deprecated public void addU64(android.renderscript.Long3);
+ method @Deprecated public void addU64(android.renderscript.Long4);
+ method @Deprecated public void addU8(short);
+ method @Deprecated public void addU8(android.renderscript.Short2);
+ method @Deprecated public void addU8(android.renderscript.Short3);
+ method @Deprecated public void addU8(android.renderscript.Short4);
+ method @Deprecated public void align(int);
+ method @Deprecated public final byte[] getData();
+ method @Deprecated public void reset();
+ method @Deprecated public void reset(int);
+ method @Deprecated public void skip(int);
+ method @Deprecated public boolean subBoolean();
+ method @Deprecated public android.renderscript.Byte2 subByte2();
+ method @Deprecated public android.renderscript.Byte3 subByte3();
+ method @Deprecated public android.renderscript.Byte4 subByte4();
+ method @Deprecated public android.renderscript.Double2 subDouble2();
+ method @Deprecated public android.renderscript.Double3 subDouble3();
+ method @Deprecated public android.renderscript.Double4 subDouble4();
+ method @Deprecated public float subF32();
+ method @Deprecated public double subF64();
+ method @Deprecated public android.renderscript.Float2 subFloat2();
+ method @Deprecated public android.renderscript.Float3 subFloat3();
+ method @Deprecated public android.renderscript.Float4 subFloat4();
+ method @Deprecated public short subI16();
+ method @Deprecated public int subI32();
+ method @Deprecated public long subI64();
+ method @Deprecated public byte subI8();
+ method @Deprecated public android.renderscript.Int2 subInt2();
+ method @Deprecated public android.renderscript.Int3 subInt3();
+ method @Deprecated public android.renderscript.Int4 subInt4();
+ method @Deprecated public android.renderscript.Long2 subLong2();
+ method @Deprecated public android.renderscript.Long3 subLong3();
+ method @Deprecated public android.renderscript.Long4 subLong4();
+ method @Deprecated public android.renderscript.Matrix2f subMatrix2f();
+ method @Deprecated public android.renderscript.Matrix3f subMatrix3f();
+ method @Deprecated public android.renderscript.Matrix4f subMatrix4f();
+ method @Deprecated public android.renderscript.Short2 subShort2();
+ method @Deprecated public android.renderscript.Short3 subShort3();
+ method @Deprecated public android.renderscript.Short4 subShort4();
+ method @Deprecated public void subalign(int);
+ }
+
+ @Deprecated public class Float2 {
+ ctor @Deprecated public Float2();
+ ctor @Deprecated public Float2(float, float);
+ field @Deprecated public float x;
+ field @Deprecated public float y;
+ }
+
+ @Deprecated public class Float3 {
+ ctor @Deprecated public Float3();
+ ctor @Deprecated public Float3(float, float, float);
+ field @Deprecated public float x;
+ field @Deprecated public float y;
+ field @Deprecated public float z;
+ }
+
+ @Deprecated public class Float4 {
+ ctor @Deprecated public Float4();
+ ctor @Deprecated public Float4(float, float, float, float);
+ field @Deprecated public float w;
+ field @Deprecated public float x;
+ field @Deprecated public float y;
+ field @Deprecated public float z;
+ }
+
+ @Deprecated public class Int2 {
+ ctor @Deprecated public Int2();
+ ctor @Deprecated public Int2(int, int);
+ field @Deprecated public int x;
+ field @Deprecated public int y;
}
- public static final class Script.LaunchOptions {
- ctor public Script.LaunchOptions();
- method public int getXEnd();
- method public int getXStart();
- method public int getYEnd();
- method public int getYStart();
- method public int getZEnd();
- method public int getZStart();
- method public android.renderscript.Script.LaunchOptions setX(int, int);
- method public android.renderscript.Script.LaunchOptions setY(int, int);
- method public android.renderscript.Script.LaunchOptions setZ(int, int);
+ @Deprecated public class Int3 {
+ ctor @Deprecated public Int3();
+ ctor @Deprecated public Int3(int, int, int);
+ field @Deprecated public int x;
+ field @Deprecated public int y;
+ field @Deprecated public int z;
}
- public class ScriptC extends android.renderscript.Script {
- ctor protected ScriptC(int, android.renderscript.RenderScript);
- ctor protected ScriptC(long, android.renderscript.RenderScript);
- ctor protected ScriptC(android.renderscript.RenderScript, android.content.res.Resources, int);
- ctor protected ScriptC(android.renderscript.RenderScript, String, byte[], byte[]);
+ @Deprecated public class Int4 {
+ ctor @Deprecated public Int4();
+ ctor @Deprecated public Int4(int, int, int, int);
+ field @Deprecated public int w;
+ field @Deprecated public int x;
+ field @Deprecated public int y;
+ field @Deprecated public int z;
+ }
+
+ @Deprecated public class Long2 {
+ ctor @Deprecated public Long2();
+ ctor @Deprecated public Long2(long, long);
+ field @Deprecated public long x;
+ field @Deprecated public long y;
+ }
+
+ @Deprecated public class Long3 {
+ ctor @Deprecated public Long3();
+ ctor @Deprecated public Long3(long, long, long);
+ field @Deprecated public long x;
+ field @Deprecated public long y;
+ field @Deprecated public long z;
+ }
+
+ @Deprecated public class Long4 {
+ ctor @Deprecated public Long4();
+ ctor @Deprecated public Long4(long, long, long, long);
+ field @Deprecated public long w;
+ field @Deprecated public long x;
+ field @Deprecated public long y;
+ field @Deprecated public long z;
+ }
+
+ @Deprecated public class Matrix2f {
+ ctor @Deprecated public Matrix2f();
+ ctor @Deprecated public Matrix2f(float[]);
+ method @Deprecated public float get(int, int);
+ method @Deprecated public float[] getArray();
+ method @Deprecated public void load(android.renderscript.Matrix2f);
+ method @Deprecated public void loadIdentity();
+ method @Deprecated public void loadMultiply(android.renderscript.Matrix2f, android.renderscript.Matrix2f);
+ method @Deprecated public void loadRotate(float);
+ method @Deprecated public void loadScale(float, float);
+ method @Deprecated public void multiply(android.renderscript.Matrix2f);
+ method @Deprecated public void rotate(float);
+ method @Deprecated public void scale(float, float);
+ method @Deprecated public void set(int, int, float);
+ method @Deprecated public void transpose();
+ }
+
+ @Deprecated public class Matrix3f {
+ ctor @Deprecated public Matrix3f();
+ ctor @Deprecated public Matrix3f(float[]);
+ method @Deprecated public float get(int, int);
+ method @Deprecated public float[] getArray();
+ method @Deprecated public void load(android.renderscript.Matrix3f);
+ method @Deprecated public void loadIdentity();
+ method @Deprecated public void loadMultiply(android.renderscript.Matrix3f, android.renderscript.Matrix3f);
+ method @Deprecated public void loadRotate(float, float, float, float);
+ method @Deprecated public void loadRotate(float);
+ method @Deprecated public void loadScale(float, float);
+ method @Deprecated public void loadScale(float, float, float);
+ method @Deprecated public void loadTranslate(float, float);
+ method @Deprecated public void multiply(android.renderscript.Matrix3f);
+ method @Deprecated public void rotate(float, float, float, float);
+ method @Deprecated public void rotate(float);
+ method @Deprecated public void scale(float, float);
+ method @Deprecated public void scale(float, float, float);
+ method @Deprecated public void set(int, int, float);
+ method @Deprecated public void translate(float, float);
+ method @Deprecated public void transpose();
+ }
+
+ @Deprecated public class Matrix4f {
+ ctor @Deprecated public Matrix4f();
+ ctor @Deprecated public Matrix4f(float[]);
+ method @Deprecated public float get(int, int);
+ method @Deprecated public float[] getArray();
+ method @Deprecated public boolean inverse();
+ method @Deprecated public boolean inverseTranspose();
+ method @Deprecated public void load(android.renderscript.Matrix4f);
+ method @Deprecated public void loadFrustum(float, float, float, float, float, float);
+ method @Deprecated public void loadIdentity();
+ method @Deprecated public void loadMultiply(android.renderscript.Matrix4f, android.renderscript.Matrix4f);
+ method @Deprecated public void loadOrtho(float, float, float, float, float, float);
+ method @Deprecated public void loadOrthoWindow(int, int);
+ method @Deprecated public void loadPerspective(float, float, float, float);
+ method @Deprecated public void loadProjectionNormalized(int, int);
+ method @Deprecated public void loadRotate(float, float, float, float);
+ method @Deprecated public void loadScale(float, float, float);
+ method @Deprecated public void loadTranslate(float, float, float);
+ method @Deprecated public void multiply(android.renderscript.Matrix4f);
+ method @Deprecated public void rotate(float, float, float, float);
+ method @Deprecated public void scale(float, float, float);
+ method @Deprecated public void set(int, int, float);
+ method @Deprecated public void translate(float, float, float);
+ method @Deprecated public void transpose();
+ }
+
+ @Deprecated public class RSDriverException extends android.renderscript.RSRuntimeException {
+ ctor @Deprecated public RSDriverException(String);
+ }
+
+ @Deprecated public class RSIllegalArgumentException extends android.renderscript.RSRuntimeException {
+ ctor @Deprecated public RSIllegalArgumentException(String);
+ }
+
+ @Deprecated public class RSInvalidStateException extends android.renderscript.RSRuntimeException {
+ ctor @Deprecated public RSInvalidStateException(String);
+ }
+
+ @Deprecated public class RSRuntimeException extends java.lang.RuntimeException {
+ ctor @Deprecated public RSRuntimeException(String);
+ }
+
+ @Deprecated public class RenderScript {
+ method @Deprecated public void contextDump();
+ method @Deprecated public static android.renderscript.RenderScript create(android.content.Context);
+ method @Deprecated public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
+ method @Deprecated public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+ method @Deprecated public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
+ method @Deprecated public void destroy();
+ method @Deprecated public void finish();
+ method @Deprecated public final android.content.Context getApplicationContext();
+ method @Deprecated public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
+ method @Deprecated public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+ method @Deprecated public static long getMinorVersion();
+ method @Deprecated public static void releaseAllContexts();
+ method @Deprecated public void sendMessage(int, int[]);
+ method @Deprecated public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
+ method @Deprecated public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
+ method @Deprecated public void setPriority(android.renderscript.RenderScript.Priority);
+ field @Deprecated public static final int CREATE_FLAG_LOW_LATENCY = 2; // 0x2
+ field @Deprecated public static final int CREATE_FLAG_LOW_POWER = 4; // 0x4
+ field @Deprecated public static final int CREATE_FLAG_NONE = 0; // 0x0
+ }
+
+ @Deprecated public enum RenderScript.ContextType {
+ enum_constant @Deprecated public static final android.renderscript.RenderScript.ContextType DEBUG;
+ enum_constant @Deprecated public static final android.renderscript.RenderScript.ContextType NORMAL;
+ enum_constant @Deprecated public static final android.renderscript.RenderScript.ContextType PROFILE;
+ }
+
+ @Deprecated public enum RenderScript.Priority {
+ enum_constant @Deprecated public static final android.renderscript.RenderScript.Priority LOW;
+ enum_constant @Deprecated public static final android.renderscript.RenderScript.Priority NORMAL;
+ }
+
+ @Deprecated public static class RenderScript.RSErrorHandler implements java.lang.Runnable {
+ ctor @Deprecated public RenderScript.RSErrorHandler();
+ method @Deprecated public void run();
+ field @Deprecated protected String mErrorMessage;
+ field @Deprecated protected int mErrorNum;
}
- public final class ScriptGroup extends android.renderscript.BaseObj {
- method public Object[] execute(java.lang.Object...);
+ @Deprecated public static class RenderScript.RSMessageHandler implements java.lang.Runnable {
+ ctor @Deprecated public RenderScript.RSMessageHandler();
+ method @Deprecated public void run();
+ field @Deprecated protected int[] mData;
+ field @Deprecated protected int mID;
+ field @Deprecated protected int mLength;
+ }
+
+ @Deprecated public class Sampler extends android.renderscript.BaseObj {
+ method @Deprecated public static android.renderscript.Sampler CLAMP_LINEAR(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler CLAMP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler CLAMP_NEAREST(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler MIRRORED_REPEAT_LINEAR(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler MIRRORED_REPEAT_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler MIRRORED_REPEAT_NEAREST(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler WRAP_LINEAR(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler WRAP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
+ method @Deprecated public static android.renderscript.Sampler WRAP_NEAREST(android.renderscript.RenderScript);
+ method @Deprecated public float getAnisotropy();
+ method @Deprecated public android.renderscript.Sampler.Value getMagnification();
+ method @Deprecated public android.renderscript.Sampler.Value getMinification();
+ method @Deprecated public android.renderscript.Sampler.Value getWrapS();
+ method @Deprecated public android.renderscript.Sampler.Value getWrapT();
+ }
+
+ @Deprecated public static class Sampler.Builder {
+ ctor @Deprecated public Sampler.Builder(android.renderscript.RenderScript);
+ method @Deprecated public android.renderscript.Sampler create();
+ method @Deprecated public void setAnisotropy(float);
+ method @Deprecated public void setMagnification(android.renderscript.Sampler.Value);
+ method @Deprecated public void setMinification(android.renderscript.Sampler.Value);
+ method @Deprecated public void setWrapS(android.renderscript.Sampler.Value);
+ method @Deprecated public void setWrapT(android.renderscript.Sampler.Value);
+ }
+
+ @Deprecated public enum Sampler.Value {
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value CLAMP;
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value LINEAR;
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value LINEAR_MIP_LINEAR;
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value LINEAR_MIP_NEAREST;
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value MIRRORED_REPEAT;
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value NEAREST;
+ enum_constant @Deprecated public static final android.renderscript.Sampler.Value WRAP;
+ }
+
+ @Deprecated public class Script extends android.renderscript.BaseObj {
+ method @Deprecated public void bindAllocation(android.renderscript.Allocation, int);
+ method @Deprecated protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+ method @Deprecated protected android.renderscript.Script.InvokeID createInvokeID(int);
+ method @Deprecated protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
+ method @Deprecated protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
+ method @Deprecated protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+ method @Deprecated protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+ method @Deprecated protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public boolean getVarB(int);
+ method @Deprecated public double getVarD(int);
+ method @Deprecated public float getVarF(int);
+ method @Deprecated public int getVarI(int);
+ method @Deprecated public long getVarJ(int);
+ method @Deprecated public void getVarV(int, android.renderscript.FieldPacker);
+ method @Deprecated protected void invoke(int);
+ method @Deprecated protected void invoke(int, android.renderscript.FieldPacker);
+ method @Deprecated protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void setTimeZone(String);
+ method @Deprecated public void setVar(int, float);
+ method @Deprecated public void setVar(int, double);
+ method @Deprecated public void setVar(int, int);
+ method @Deprecated public void setVar(int, long);
+ method @Deprecated public void setVar(int, boolean);
+ method @Deprecated public void setVar(int, android.renderscript.BaseObj);
+ method @Deprecated public void setVar(int, android.renderscript.FieldPacker);
+ method @Deprecated public void setVar(int, android.renderscript.FieldPacker, android.renderscript.Element, int[]);
+ }
+
+ @Deprecated public static class Script.Builder {
+ }
+
+ @Deprecated public static class Script.FieldBase {
+ ctor @Deprecated protected Script.FieldBase();
+ method @Deprecated public android.renderscript.Allocation getAllocation();
+ method @Deprecated public android.renderscript.Element getElement();
+ method @Deprecated public android.renderscript.Type getType();
+ method @Deprecated protected void init(android.renderscript.RenderScript, int);
+ method @Deprecated protected void init(android.renderscript.RenderScript, int, int);
+ method @Deprecated public void updateAllocation();
+ field @Deprecated protected android.renderscript.Allocation mAllocation;
+ field @Deprecated protected android.renderscript.Element mElement;
+ }
+
+ @Deprecated public static final class Script.FieldID extends android.renderscript.BaseObj {
+ }
+
+ @Deprecated public static final class Script.InvokeID extends android.renderscript.BaseObj {
+ }
+
+ @Deprecated public static final class Script.KernelID extends android.renderscript.BaseObj {
+ }
+
+ @Deprecated public static final class Script.LaunchOptions {
+ ctor @Deprecated public Script.LaunchOptions();
+ method @Deprecated public int getXEnd();
+ method @Deprecated public int getXStart();
+ method @Deprecated public int getYEnd();
+ method @Deprecated public int getYStart();
+ method @Deprecated public int getZEnd();
+ method @Deprecated public int getZStart();
+ method @Deprecated public android.renderscript.Script.LaunchOptions setX(int, int);
+ method @Deprecated public android.renderscript.Script.LaunchOptions setY(int, int);
+ method @Deprecated public android.renderscript.Script.LaunchOptions setZ(int, int);
+ }
+
+ @Deprecated public class ScriptC extends android.renderscript.Script {
+ ctor @Deprecated protected ScriptC(int, android.renderscript.RenderScript);
+ ctor @Deprecated protected ScriptC(long, android.renderscript.RenderScript);
+ ctor @Deprecated protected ScriptC(android.renderscript.RenderScript, android.content.res.Resources, int);
+ ctor @Deprecated protected ScriptC(android.renderscript.RenderScript, String, byte[], byte[]);
+ }
+
+ @Deprecated public final class ScriptGroup extends android.renderscript.BaseObj {
+ method @Deprecated public Object[] execute(java.lang.Object...);
method @Deprecated public void execute();
method @Deprecated public void setInput(android.renderscript.Script.KernelID, android.renderscript.Allocation);
method @Deprecated public void setOutput(android.renderscript.Script.KernelID, android.renderscript.Allocation);
}
- public static final class ScriptGroup.Binding {
- ctor public ScriptGroup.Binding(android.renderscript.Script.FieldID, Object);
+ @Deprecated public static final class ScriptGroup.Binding {
+ ctor @Deprecated public ScriptGroup.Binding(android.renderscript.Script.FieldID, Object);
}
@Deprecated public static final class ScriptGroup.Builder {
@@ -36376,336 +36385,336 @@ package android.renderscript {
method @Deprecated public android.renderscript.ScriptGroup create();
}
- public static final class ScriptGroup.Builder2 {
- ctor public ScriptGroup.Builder2(android.renderscript.RenderScript);
- method public android.renderscript.ScriptGroup.Input addInput();
- method public android.renderscript.ScriptGroup.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
- method public android.renderscript.ScriptGroup.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
- method public android.renderscript.ScriptGroup create(String, android.renderscript.ScriptGroup.Future...);
- }
-
- public static final class ScriptGroup.Closure extends android.renderscript.BaseObj {
- method public android.renderscript.ScriptGroup.Future getGlobal(android.renderscript.Script.FieldID);
- method public android.renderscript.ScriptGroup.Future getReturn();
- }
-
- public static final class ScriptGroup.Future {
- }
-
- public static final class ScriptGroup.Input {
- }
-
- public abstract class ScriptIntrinsic extends android.renderscript.Script {
- }
-
- public final class ScriptIntrinsic3DLUT extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsic3DLUT create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setLUT(android.renderscript.Allocation);
- }
-
- public final class ScriptIntrinsicBLAS extends android.renderscript.ScriptIntrinsic {
- method public void BNNM(android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation, int, int);
- method public void CGBMV(int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
- method public void CGEMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
- method public void CGEMV(int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
- method public void CGERC(android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void CGERU(android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void CHBMV(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
- method public void CHEMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
- method public void CHEMV(int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
- method public void CHER(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void CHER2(int, android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void CHER2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
- method public void CHERK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
- method public void CHPMV(int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
- method public void CHPR(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void CHPR2(int, android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void CSYMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
- method public void CSYR2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
- method public void CSYRK(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
- method public void CTBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void CTBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void CTPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void CTPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void CTRMM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void CTRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void CTRSM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void CTRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void DGBMV(int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
- method public void DGEMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
- method public void DGEMV(int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
- method public void DGER(double, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void DSBMV(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
- method public void DSPMV(int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
- method public void DSPR(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void DSPR2(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void DSYMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
- method public void DSYMV(int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
- method public void DSYR(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void DSYR2(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void DSYR2K(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
- method public void DSYRK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
- method public void DTBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void DTBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void DTPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void DTPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void DTRMM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void DTRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void DTRSM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void DTRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void SGBMV(int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
- method public void SGEMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
- method public void SGEMV(int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
- method public void SGER(float, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void SSBMV(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
- method public void SSPMV(int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
- method public void SSPR(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void SSPR2(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void SSYMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
- method public void SSYMV(int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
- method public void SSYR(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void SSYR2(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void SSYR2K(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
- method public void SSYRK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
- method public void STBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void STBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void STPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void STPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void STRMM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void STRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void STRSM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void STRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void ZGBMV(int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
- method public void ZGEMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
- method public void ZGEMV(int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
- method public void ZGERC(android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void ZGERU(android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void ZHBMV(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
- method public void ZHEMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
- method public void ZHEMV(int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
- method public void ZHER(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void ZHER2(int, android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void ZHER2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
- method public void ZHERK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
- method public void ZHPMV(int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
- method public void ZHPR(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void ZHPR2(int, android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
- method public void ZSYMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
- method public void ZSYR2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
- method public void ZSYRK(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
- method public void ZTBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void ZTBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void ZTPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void ZTPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void ZTRMM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void ZTRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public void ZTRSM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
- method public void ZTRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
- method public static android.renderscript.ScriptIntrinsicBLAS create(android.renderscript.RenderScript);
- field public static final int CONJ_TRANSPOSE = 113; // 0x71
- field public static final int LEFT = 141; // 0x8d
- field public static final int LOWER = 122; // 0x7a
- field public static final int NON_UNIT = 131; // 0x83
- field public static final int NO_TRANSPOSE = 111; // 0x6f
- field public static final int RIGHT = 142; // 0x8e
- field public static final int TRANSPOSE = 112; // 0x70
- field public static final int UNIT = 132; // 0x84
- field public static final int UPPER = 121; // 0x79
- }
-
- public class ScriptIntrinsicBlend extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicBlend create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachClear(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachClear(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachDst(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachDst(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachDstAtop(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachDstAtop(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachDstIn(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachDstIn(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachDstOut(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachDstOut(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachDstOver(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachDstOver(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachMultiply(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachMultiply(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachSrc(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachSrc(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachSrcAtop(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachSrcAtop(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachSrcIn(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachSrcIn(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachSrcOut(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachSrcOut(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachSrcOver(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachSrcOver(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachSubtract(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachSubtract(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEachXor(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEachXor(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.KernelID getKernelIDAdd();
- method public android.renderscript.Script.KernelID getKernelIDClear();
- method public android.renderscript.Script.KernelID getKernelIDDst();
- method public android.renderscript.Script.KernelID getKernelIDDstAtop();
- method public android.renderscript.Script.KernelID getKernelIDDstIn();
- method public android.renderscript.Script.KernelID getKernelIDDstOut();
- method public android.renderscript.Script.KernelID getKernelIDDstOver();
- method public android.renderscript.Script.KernelID getKernelIDMultiply();
- method public android.renderscript.Script.KernelID getKernelIDSrc();
- method public android.renderscript.Script.KernelID getKernelIDSrcAtop();
- method public android.renderscript.Script.KernelID getKernelIDSrcIn();
- method public android.renderscript.Script.KernelID getKernelIDSrcOut();
- method public android.renderscript.Script.KernelID getKernelIDSrcOver();
- method public android.renderscript.Script.KernelID getKernelIDSubtract();
- method public android.renderscript.Script.KernelID getKernelIDXor();
- }
-
- public final class ScriptIntrinsicBlur extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicBlur create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.FieldID getFieldID_Input();
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setInput(android.renderscript.Allocation);
- method public void setRadius(float);
- }
-
- public final class ScriptIntrinsicColorMatrix extends android.renderscript.ScriptIntrinsic {
+ @Deprecated public static final class ScriptGroup.Builder2 {
+ ctor @Deprecated public ScriptGroup.Builder2(android.renderscript.RenderScript);
+ method @Deprecated public android.renderscript.ScriptGroup.Input addInput();
+ method @Deprecated public android.renderscript.ScriptGroup.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
+ method @Deprecated public android.renderscript.ScriptGroup.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
+ method @Deprecated public android.renderscript.ScriptGroup create(String, android.renderscript.ScriptGroup.Future...);
+ }
+
+ @Deprecated public static final class ScriptGroup.Closure extends android.renderscript.BaseObj {
+ method @Deprecated public android.renderscript.ScriptGroup.Future getGlobal(android.renderscript.Script.FieldID);
+ method @Deprecated public android.renderscript.ScriptGroup.Future getReturn();
+ }
+
+ @Deprecated public static final class ScriptGroup.Future {
+ }
+
+ @Deprecated public static final class ScriptGroup.Input {
+ }
+
+ @Deprecated public abstract class ScriptIntrinsic extends android.renderscript.Script {
+ }
+
+ @Deprecated public final class ScriptIntrinsic3DLUT extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsic3DLUT create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setLUT(android.renderscript.Allocation);
+ }
+
+ @Deprecated public final class ScriptIntrinsicBLAS extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public void BNNM(android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation, int, int);
+ method @Deprecated public void CGBMV(int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
+ method @Deprecated public void CGEMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+ method @Deprecated public void CGEMV(int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
+ method @Deprecated public void CGERC(android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void CGERU(android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void CHBMV(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
+ method @Deprecated public void CHEMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+ method @Deprecated public void CHEMV(int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
+ method @Deprecated public void CHER(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void CHER2(int, android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void CHER2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+ method @Deprecated public void CHERK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
+ method @Deprecated public void CHPMV(int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Float2, android.renderscript.Allocation, int);
+ method @Deprecated public void CHPR(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void CHPR2(int, android.renderscript.Float2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void CSYMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+ method @Deprecated public void CSYR2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+ method @Deprecated public void CSYRK(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+ method @Deprecated public void CTBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void CTBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void CTPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void CTPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void CTRMM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void CTRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void CTRSM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void CTRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void DGBMV(int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
+ method @Deprecated public void DGEMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+ method @Deprecated public void DGEMV(int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
+ method @Deprecated public void DGER(double, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void DSBMV(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
+ method @Deprecated public void DSPMV(int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
+ method @Deprecated public void DSPR(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void DSPR2(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void DSYMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+ method @Deprecated public void DSYMV(int, double, android.renderscript.Allocation, android.renderscript.Allocation, int, double, android.renderscript.Allocation, int);
+ method @Deprecated public void DSYR(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void DSYR2(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void DSYR2K(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+ method @Deprecated public void DSYRK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
+ method @Deprecated public void DTBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void DTBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void DTPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void DTPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void DTRMM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void DTRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void DTRSM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void DTRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void SGBMV(int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
+ method @Deprecated public void SGEMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+ method @Deprecated public void SGEMV(int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
+ method @Deprecated public void SGER(float, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void SSBMV(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
+ method @Deprecated public void SSPMV(int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
+ method @Deprecated public void SSPR(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void SSPR2(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void SSYMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+ method @Deprecated public void SSYMV(int, float, android.renderscript.Allocation, android.renderscript.Allocation, int, float, android.renderscript.Allocation, int);
+ method @Deprecated public void SSYR(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void SSYR2(int, float, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void SSYR2K(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+ method @Deprecated public void SSYRK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
+ method @Deprecated public void STBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void STBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void STPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void STPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void STRMM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void STRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void STRSM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void STRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void ZGBMV(int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
+ method @Deprecated public void ZGEMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+ method @Deprecated public void ZGEMV(int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
+ method @Deprecated public void ZGERC(android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void ZGERU(android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void ZHBMV(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
+ method @Deprecated public void ZHEMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+ method @Deprecated public void ZHEMV(int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
+ method @Deprecated public void ZHER(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void ZHER2(int, android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void ZHER2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+ method @Deprecated public void ZHERK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
+ method @Deprecated public void ZHPMV(int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, int, android.renderscript.Double2, android.renderscript.Allocation, int);
+ method @Deprecated public void ZHPR(int, double, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void ZHPR2(int, android.renderscript.Double2, android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation);
+ method @Deprecated public void ZSYMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+ method @Deprecated public void ZSYR2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+ method @Deprecated public void ZSYRK(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+ method @Deprecated public void ZTBMV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void ZTBSV(int, int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void ZTPMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void ZTPSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void ZTRMM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void ZTRMV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public void ZTRSM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void ZTRSV(int, int, int, android.renderscript.Allocation, android.renderscript.Allocation, int);
+ method @Deprecated public static android.renderscript.ScriptIntrinsicBLAS create(android.renderscript.RenderScript);
+ field @Deprecated public static final int CONJ_TRANSPOSE = 113; // 0x71
+ field @Deprecated public static final int LEFT = 141; // 0x8d
+ field @Deprecated public static final int LOWER = 122; // 0x7a
+ field @Deprecated public static final int NON_UNIT = 131; // 0x83
+ field @Deprecated public static final int NO_TRANSPOSE = 111; // 0x6f
+ field @Deprecated public static final int RIGHT = 142; // 0x8e
+ field @Deprecated public static final int TRANSPOSE = 112; // 0x70
+ field @Deprecated public static final int UNIT = 132; // 0x84
+ field @Deprecated public static final int UPPER = 121; // 0x79
+ }
+
+ @Deprecated public class ScriptIntrinsicBlend extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicBlend create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachClear(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachClear(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachDst(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachDst(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachDstAtop(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachDstAtop(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachDstIn(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachDstIn(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachDstOut(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachDstOut(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachDstOver(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachDstOver(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachMultiply(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachMultiply(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachSrc(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachSrc(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachSrcAtop(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachSrcAtop(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachSrcIn(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachSrcIn(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachSrcOut(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachSrcOut(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachSrcOver(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachSrcOver(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachSubtract(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachSubtract(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEachXor(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEachXor(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDAdd();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDClear();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDDst();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDDstAtop();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDDstIn();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDDstOut();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDDstOver();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDMultiply();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDSrc();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDSrcAtop();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDSrcIn();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDSrcOut();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDSrcOver();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDSubtract();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelIDXor();
+ }
+
+ @Deprecated public final class ScriptIntrinsicBlur extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicBlur create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.FieldID getFieldID_Input();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setInput(android.renderscript.Allocation);
+ method @Deprecated public void setRadius(float);
+ }
+
+ @Deprecated public final class ScriptIntrinsicColorMatrix extends android.renderscript.ScriptIntrinsic {
method @Deprecated public static android.renderscript.ScriptIntrinsicColorMatrix create(android.renderscript.RenderScript, android.renderscript.Element);
- method public static android.renderscript.ScriptIntrinsicColorMatrix create(android.renderscript.RenderScript);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setAdd(android.renderscript.Float4);
- method public void setAdd(float, float, float, float);
- method public void setColorMatrix(android.renderscript.Matrix4f);
- method public void setColorMatrix(android.renderscript.Matrix3f);
- method public void setGreyscale();
- method public void setRGBtoYUV();
- method public void setYUVtoRGB();
- }
-
- public final class ScriptIntrinsicConvolve3x3 extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicConvolve3x3 create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.FieldID getFieldID_Input();
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setCoefficients(float[]);
- method public void setInput(android.renderscript.Allocation);
- }
-
- public final class ScriptIntrinsicConvolve5x5 extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicConvolve5x5 create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.FieldID getFieldID_Input();
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setCoefficients(float[]);
- method public void setInput(android.renderscript.Allocation);
- }
-
- public final class ScriptIntrinsicHistogram extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicHistogram create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public void forEach_Dot(android.renderscript.Allocation);
- method public void forEach_Dot(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.FieldID getFieldID_Input();
- method public android.renderscript.Script.KernelID getKernelID_Separate();
- method public void setDotCoefficients(float, float, float, float);
- method public void setOutput(android.renderscript.Allocation);
- }
-
- public final class ScriptIntrinsicLUT extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicLUT create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
- method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setAlpha(int, int);
- method public void setBlue(int, int);
- method public void setGreen(int, int);
- method public void setRed(int, int);
- }
-
- public final class ScriptIntrinsicResize extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicResize create(android.renderscript.RenderScript);
- method public void forEach_bicubic(android.renderscript.Allocation);
- method public void forEach_bicubic(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
- method public android.renderscript.Script.FieldID getFieldID_Input();
- method public android.renderscript.Script.KernelID getKernelID_bicubic();
- method public void setInput(android.renderscript.Allocation);
- }
-
- public final class ScriptIntrinsicYuvToRGB extends android.renderscript.ScriptIntrinsic {
- method public static android.renderscript.ScriptIntrinsicYuvToRGB create(android.renderscript.RenderScript, android.renderscript.Element);
- method public void forEach(android.renderscript.Allocation);
- method public android.renderscript.Script.FieldID getFieldID_Input();
- method public android.renderscript.Script.KernelID getKernelID();
- method public void setInput(android.renderscript.Allocation);
- }
-
- public class Short2 {
- ctor public Short2();
- ctor public Short2(short, short);
- field public short x;
- field public short y;
- }
-
- public class Short3 {
- ctor public Short3();
- ctor public Short3(short, short, short);
- field public short x;
- field public short y;
- field public short z;
- }
-
- public class Short4 {
- ctor public Short4();
- ctor public Short4(short, short, short, short);
- field public short w;
- field public short x;
- field public short y;
- field public short z;
- }
-
- public class Type extends android.renderscript.BaseObj {
- method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
- method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
- method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
- method public int getCount();
- method public android.renderscript.Element getElement();
- method public int getX();
- method public int getY();
- method public int getYuv();
- method public int getZ();
- method public boolean hasFaces();
- method public boolean hasMipmaps();
- }
-
- public static class Type.Builder {
- ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
- method public android.renderscript.Type create();
- method public android.renderscript.Type.Builder setFaces(boolean);
- method public android.renderscript.Type.Builder setMipmaps(boolean);
- method public android.renderscript.Type.Builder setX(int);
- method public android.renderscript.Type.Builder setY(int);
- method public android.renderscript.Type.Builder setYuvFormat(int);
- method public android.renderscript.Type.Builder setZ(int);
- }
-
- public enum Type.CubemapFace {
- enum_constant public static final android.renderscript.Type.CubemapFace NEGATIVE_X;
- enum_constant public static final android.renderscript.Type.CubemapFace NEGATIVE_Y;
- enum_constant public static final android.renderscript.Type.CubemapFace NEGATIVE_Z;
- enum_constant public static final android.renderscript.Type.CubemapFace POSITIVE_X;
- enum_constant public static final android.renderscript.Type.CubemapFace POSITIVE_Y;
- enum_constant public static final android.renderscript.Type.CubemapFace POSITIVE_Z;
+ method @Deprecated public static android.renderscript.ScriptIntrinsicColorMatrix create(android.renderscript.RenderScript);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setAdd(android.renderscript.Float4);
+ method @Deprecated public void setAdd(float, float, float, float);
+ method @Deprecated public void setColorMatrix(android.renderscript.Matrix4f);
+ method @Deprecated public void setColorMatrix(android.renderscript.Matrix3f);
+ method @Deprecated public void setGreyscale();
+ method @Deprecated public void setRGBtoYUV();
+ method @Deprecated public void setYUVtoRGB();
+ }
+
+ @Deprecated public final class ScriptIntrinsicConvolve3x3 extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicConvolve3x3 create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.FieldID getFieldID_Input();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setCoefficients(float[]);
+ method @Deprecated public void setInput(android.renderscript.Allocation);
+ }
+
+ @Deprecated public final class ScriptIntrinsicConvolve5x5 extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicConvolve5x5 create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.FieldID getFieldID_Input();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setCoefficients(float[]);
+ method @Deprecated public void setInput(android.renderscript.Allocation);
+ }
+
+ @Deprecated public final class ScriptIntrinsicHistogram extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicHistogram create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public void forEach_Dot(android.renderscript.Allocation);
+ method @Deprecated public void forEach_Dot(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.FieldID getFieldID_Input();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID_Separate();
+ method @Deprecated public void setDotCoefficients(float, float, float, float);
+ method @Deprecated public void setOutput(android.renderscript.Allocation);
+ }
+
+ @Deprecated public final class ScriptIntrinsicLUT extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicLUT create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
+ method @Deprecated public void forEach(android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setAlpha(int, int);
+ method @Deprecated public void setBlue(int, int);
+ method @Deprecated public void setGreen(int, int);
+ method @Deprecated public void setRed(int, int);
+ }
+
+ @Deprecated public final class ScriptIntrinsicResize extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicResize create(android.renderscript.RenderScript);
+ method @Deprecated public void forEach_bicubic(android.renderscript.Allocation);
+ method @Deprecated public void forEach_bicubic(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method @Deprecated public android.renderscript.Script.FieldID getFieldID_Input();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID_bicubic();
+ method @Deprecated public void setInput(android.renderscript.Allocation);
+ }
+
+ @Deprecated public final class ScriptIntrinsicYuvToRGB extends android.renderscript.ScriptIntrinsic {
+ method @Deprecated public static android.renderscript.ScriptIntrinsicYuvToRGB create(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public void forEach(android.renderscript.Allocation);
+ method @Deprecated public android.renderscript.Script.FieldID getFieldID_Input();
+ method @Deprecated public android.renderscript.Script.KernelID getKernelID();
+ method @Deprecated public void setInput(android.renderscript.Allocation);
+ }
+
+ @Deprecated public class Short2 {
+ ctor @Deprecated public Short2();
+ ctor @Deprecated public Short2(short, short);
+ field @Deprecated public short x;
+ field @Deprecated public short y;
+ }
+
+ @Deprecated public class Short3 {
+ ctor @Deprecated public Short3();
+ ctor @Deprecated public Short3(short, short, short);
+ field @Deprecated public short x;
+ field @Deprecated public short y;
+ field @Deprecated public short z;
+ }
+
+ @Deprecated public class Short4 {
+ ctor @Deprecated public Short4();
+ ctor @Deprecated public Short4(short, short, short, short);
+ field @Deprecated public short w;
+ field @Deprecated public short x;
+ field @Deprecated public short y;
+ field @Deprecated public short z;
+ }
+
+ @Deprecated public class Type extends android.renderscript.BaseObj {
+ method @Deprecated public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
+ method @Deprecated public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
+ method @Deprecated public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+ method @Deprecated public int getCount();
+ method @Deprecated public android.renderscript.Element getElement();
+ method @Deprecated public int getX();
+ method @Deprecated public int getY();
+ method @Deprecated public int getYuv();
+ method @Deprecated public int getZ();
+ method @Deprecated public boolean hasFaces();
+ method @Deprecated public boolean hasMipmaps();
+ }
+
+ @Deprecated public static class Type.Builder {
+ ctor @Deprecated public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
+ method @Deprecated public android.renderscript.Type create();
+ method @Deprecated public android.renderscript.Type.Builder setFaces(boolean);
+ method @Deprecated public android.renderscript.Type.Builder setMipmaps(boolean);
+ method @Deprecated public android.renderscript.Type.Builder setX(int);
+ method @Deprecated public android.renderscript.Type.Builder setY(int);
+ method @Deprecated public android.renderscript.Type.Builder setYuvFormat(int);
+ method @Deprecated public android.renderscript.Type.Builder setZ(int);
+ }
+
+ @Deprecated public enum Type.CubemapFace {
+ enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace NEGATIVE_X;
+ enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace NEGATIVE_Y;
+ enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace NEGATIVE_Z;
+ enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace POSITIVE_X;
+ enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace POSITIVE_Y;
+ enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace POSITIVE_Z;
enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace POSITVE_X;
enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace POSITVE_Y;
enum_constant @Deprecated public static final android.renderscript.Type.CubemapFace POSITVE_Z;
@@ -36867,11 +36876,12 @@ package android.security {
method @NonNull public static android.content.Intent createInstallIntent();
method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy);
method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
- method @NonNull public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
+ method @NonNull @WorkerThread public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String);
- method public static boolean isCredentialManagementApp(@NonNull android.content.Context);
+ method @WorkerThread public static boolean isCredentialManagementApp(@NonNull android.content.Context);
method public static boolean isKeyAlgorithmSupported(@NonNull String);
+ method @RequiresPermission(value="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP", conditional=true) @WorkerThread public static boolean removeCredentialManagementApp(@NonNull android.content.Context);
field public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED";
field public static final String ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED";
field @Deprecated public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
@@ -37391,12 +37401,15 @@ package android.service.autofill {
method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
}
public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -37497,6 +37510,7 @@ package android.service.autofill {
method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
@@ -37524,6 +37538,7 @@ package android.service.autofill {
public final class InlinePresentation implements android.os.Parcelable {
ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.widget.inline.InlinePresentationSpec, boolean);
+ method @NonNull public static android.service.autofill.InlinePresentation createTooltipPresentation(@NonNull android.app.slice.Slice, @NonNull android.widget.inline.InlinePresentationSpec);
method public int describeContents();
method @NonNull public android.widget.inline.InlinePresentationSpec getInlinePresentationSpec();
method @NonNull public android.app.slice.Slice getSlice();
@@ -39604,22 +39619,22 @@ package android.telecom {
method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
method public final void connectionServiceFocusReleased();
method @Nullable public final android.telecom.RemoteConference createRemoteIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
- method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method @Nullable public final android.telecom.RemoteConnection createRemoteIncomingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
method @Nullable public final android.telecom.RemoteConference createRemoteOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
- method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method @Nullable public final android.telecom.RemoteConnection createRemoteOutgoingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
method public final java.util.Collection<android.telecom.Conference> getAllConferences();
method public final java.util.Collection<android.telecom.Connection> getAllConnections();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public void onConnectionServiceFocusGained();
method public void onConnectionServiceFocusLost();
- method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+ method @Nullable public android.telecom.Conference onCreateIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+ method @Nullable public android.telecom.Conference onCreateOutgoingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConferenceFailed(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -40370,6 +40385,7 @@ package android.telephony {
field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
field public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int";
+ field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool";
field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
@@ -40519,6 +40535,7 @@ package android.telephony {
field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
+ field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -40536,6 +40553,9 @@ package android.telephony {
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
+ field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
+ field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
+ field public static final String KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL = "supports_sdp_negotiation_of_d2d_rtp_header_extensions_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool";
field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
@@ -40561,6 +40581,7 @@ package android.telephony {
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+ field public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL = "vt_upgrade_supported_for_downgraded_rtt_call";
field public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
field public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
field public static final String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
@@ -40614,14 +40635,6 @@ package android.telephony {
public static final class CarrierConfigManager.Iwlan {
field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
- field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
- field public static final int DH_GROUP_1536_BIT_MODP = 5; // 0x5
- field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
- field public static final int DH_GROUP_3072_BIT_MODP = 15; // 0xf
- field public static final int DH_GROUP_4096_BIT_MODP = 16; // 0x10
- field public static final int DH_GROUP_NONE = 0; // 0x0
- field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
- field public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13; // 0xd
field public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; // 0x3
field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
@@ -40629,12 +40642,6 @@ package android.telephony {
field public static final int ID_TYPE_FQDN = 2; // 0x2
field public static final int ID_TYPE_KEY_ID = 11; // 0xb
field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
- field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
- field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
field public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL = "iwlan.add_ke_to_child_session_rekey_bool";
field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int";
field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int";
@@ -40654,10 +40661,6 @@ package android.telephony {
field public static final String KEY_IKE_REMOTE_ID_TYPE_INT = "iwlan.ike_remote_id_type_int";
field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array";
field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_ctr_key_size_int_array";
- field public static final int KEY_LEN_AES_128 = 128; // 0x80
- field public static final int KEY_LEN_AES_192 = 192; // 0xc0
- field public static final int KEY_LEN_AES_256 = 256; // 0x100
- field public static final int KEY_LEN_UNUSED = 0; // 0x0
field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int";
field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array";
field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int";
@@ -40667,11 +40670,6 @@ package android.telephony {
field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
- field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
- field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
- field public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5; // 0x5
- field public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6; // 0x6
- field public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7; // 0x7
}
public abstract class CellIdentity implements android.os.Parcelable {
@@ -42040,6 +42038,7 @@ package android.telephony {
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
+ method public int getDeviceToDeviceStatusSharing(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
method public static int getSlotIndex(int);
method @Nullable public int[] getSubscriptionIds(int);
@@ -42052,6 +42051,7 @@ package android.telephony {
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
@@ -42063,6 +42063,11 @@ package android.telephony {
field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
field public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+ field public static final int D2D_SHARING_ALL = 3; // 0x3
+ field public static final int D2D_SHARING_ALL_CONTACTS = 1; // 0x1
+ field public static final int D2D_SHARING_DISABLED = 0; // 0x0
+ field public static final int D2D_SHARING_STARRED_CONTACTS = 2; // 0x2
+ field public static final String D2D_STATUS_SHARING = "d2d_sharing_status";
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
@@ -42185,6 +42190,10 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
}
+ public static interface TelephonyCallback.PhysicalChannelConfigListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+ }
+
public static interface TelephonyCallback.PreciseDataConnectionStateListener {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
}
@@ -42912,7 +42921,7 @@ package android.telephony.ims {
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled();
- method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabledByUser() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabled() throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
@@ -46569,9 +46578,9 @@ package android.view {
public class GestureDetector {
ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener, android.os.Handler);
ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener);
- ctor public GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener);
- ctor public GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
- ctor public GestureDetector(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler, boolean);
+ ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener);
+ ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
+ ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler, boolean);
method public boolean isLongpressEnabled();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
method public boolean onTouchEvent(android.view.MotionEvent);
@@ -47260,7 +47269,7 @@ package android.view {
method public abstract android.view.LayoutInflater cloneInContext(android.content.Context);
method public final android.view.View createView(String, String, android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException;
method @Nullable public final android.view.View createView(@NonNull android.content.Context, @NonNull String, @Nullable String, @Nullable android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException;
- method public static android.view.LayoutInflater from(android.content.Context);
+ method public static android.view.LayoutInflater from(@UiContext android.content.Context);
method public android.content.Context getContext();
method public final android.view.LayoutInflater.Factory getFactory();
method public final android.view.LayoutInflater.Factory2 getFactory2();
@@ -48154,7 +48163,7 @@ package android.view {
method public final boolean getClipToOutline();
method @Nullable public final android.view.contentcapture.ContentCaptureSession getContentCaptureSession();
method public CharSequence getContentDescription();
- method public final android.content.Context getContext();
+ method @UiContext public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
method public final boolean getDefaultFocusHighlightEnabled();
method public static int getDefaultSize(int, int);
@@ -48953,7 +48962,7 @@ package android.view {
public class ViewConfiguration {
ctor @Deprecated public ViewConfiguration();
- method public static android.view.ViewConfiguration get(android.content.Context);
+ method public static android.view.ViewConfiguration get(@UiContext android.content.Context);
method @Deprecated @FloatRange(from=1.0) public static float getAmbiguousGestureMultiplier();
method public static long getDefaultActionModeHideDuration();
method public static int getDoubleTapTimeout();
@@ -49511,7 +49520,7 @@ package android.view {
}
public abstract class Window {
- ctor public Window(android.content.Context);
+ ctor public Window(@UiContext android.content.Context);
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void addFlags(int);
method public final void addOnFrameMetricsAvailableListener(@NonNull android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler);
@@ -49526,7 +49535,7 @@ package android.view {
method public int getColorMode();
method public final android.view.Window getContainer();
method public android.transition.Scene getContentScene();
- method public final android.content.Context getContext();
+ method @UiContext public final android.content.Context getContext();
method @Nullable public abstract android.view.View getCurrentFocus();
method @NonNull public abstract android.view.View getDecorView();
method public static int getDefaultFeatures(android.content.Context);
@@ -49895,6 +49904,7 @@ package android.view {
public interface WindowManager extends android.view.ViewManager {
method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
method @Deprecated public android.view.Display getDefaultDisplay();
method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
@@ -51331,6 +51341,7 @@ package android.view.inputmethod {
method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getHostPackageName();
method @NonNull public java.util.List<android.widget.inline.InlinePresentationSpec> getInlinePresentationSpecs();
+ method @Nullable public android.widget.inline.InlinePresentationSpec getInlineTooltipPresentationSpec();
method public int getMaxSuggestionCount();
method @NonNull public android.os.LocaleList getSupportedLocales();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -51342,9 +51353,12 @@ package android.view.inputmethod {
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
@@ -51486,6 +51500,7 @@ package android.view.inputmethod {
method public int getSubtypeCount();
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
+ method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
}
@@ -51507,6 +51522,7 @@ package android.view.inputmethod {
method public boolean isActive(android.view.View);
method public boolean isActive();
method public boolean isFullscreenMode();
+ method public boolean isInputMethodSuppressingSpellChecker();
method @Deprecated public boolean isWatchingCursor(android.view.View);
method public void restartInput(android.view.View);
method public void sendAppPrivateCommand(android.view.View, String, android.os.Bundle);
@@ -51927,6 +51943,7 @@ package android.view.textclassifier {
field public static final String TYPE_PHONE = "phone";
field public static final String TYPE_UNKNOWN = "";
field public static final String TYPE_URL = "url";
+ field public static final String WIDGET_TYPE_CLIPBOARD = "clipboard";
field public static final String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
field public static final String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
field public static final String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
@@ -52403,6 +52420,17 @@ package android.view.translation {
method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
}
+ public final class UiTranslationManager {
+ method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback);
+ method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback);
+ }
+
+ public interface UiTranslationStateCallback {
+ method public void onFinished();
+ method public void onPaused();
+ method public void onStarted(@NonNull String, @NonNull String);
+ }
+
public final class ViewTranslationRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.autofill.AutofillId getAutofillId();
@@ -54982,6 +55010,7 @@ package android.widget {
ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
ctor public RemoteViews(android.widget.RemoteViews);
ctor public RemoteViews(android.os.Parcel);
+ method public void addStableView(@IdRes int, @NonNull android.widget.RemoteViews, int);
method public void addView(@IdRes int, android.widget.RemoteViews);
method public android.view.View apply(android.content.Context, android.view.ViewGroup);
method @Deprecated public android.widget.RemoteViews clone();
@@ -55040,6 +55069,7 @@ package android.widget {
method public void setRelativeScrollPosition(@IdRes int, int);
method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
method public void setRemoteAdapter(@IdRes int, android.content.Intent);
+ method public void setRemoteAdapter(@IdRes int, @NonNull android.widget.RemoteViews.RemoteCollectionItems);
method public void setScrollPosition(@IdRes int, int);
method public void setShort(@IdRes int, String, short);
method public void setString(@IdRes int, String, String);
@@ -55079,6 +55109,25 @@ package android.widget {
ctor public RemoteViews.ActionException(String);
}
+ public static final class RemoteViews.RemoteCollectionItems implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getItemCount();
+ method public long getItemId(int);
+ method @NonNull public android.widget.RemoteViews getItemView(int);
+ method public int getViewTypeCount();
+ method public boolean hasStableIds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews.RemoteCollectionItems> CREATOR;
+ }
+
+ public static final class RemoteViews.RemoteCollectionItems.Builder {
+ ctor public RemoteViews.RemoteCollectionItems.Builder();
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder addItem(long, @NonNull android.widget.RemoteViews);
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems build();
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder setHasStableIds(boolean);
+ method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder setViewTypeCount(int);
+ }
+
public static class RemoteViews.RemoteResponse {
ctor public RemoteViews.RemoteResponse();
method @NonNull public android.widget.RemoteViews.RemoteResponse addSharedElement(@IdRes int, @NonNull String);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index c7d40585dd00..bc04815158ff 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -55,8 +55,17 @@ package android.app.usage {
package android.content {
+ public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+ method @NonNull public static android.net.Uri createContentUriAsUser(@NonNull android.net.Uri, @NonNull android.os.UserHandle);
+ }
+
public abstract class Context {
method @NonNull public android.os.UserHandle getUser();
+ field public static final String TEST_NETWORK_SERVICE = "test_network";
+ }
+
+ public class Intent implements java.lang.Cloneable android.os.Parcelable {
+ field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
}
}
@@ -183,6 +192,25 @@ package android.net {
method public int getResourceId();
}
+ public class NetworkPolicyManager {
+ method @NonNull public static String blockedReasonsToString(int);
+ method public static boolean isUidBlocked(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
+ field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000
+ field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000
+ field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000
+ field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4
+ field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1
+ field public static final int BLOCKED_REASON_DOZE = 2; // 0x2
+ field public static final int BLOCKED_REASON_NONE = 0; // 0x0
+ field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
+ }
+
+ public static interface NetworkPolicyManager.NetworkPolicyCallback {
+ method public default void onUidBlockedReasonChanged(int, int);
+ }
+
public final class NetworkStateSnapshot implements android.os.Parcelable {
ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int);
method public int describeContents();
@@ -199,6 +227,16 @@ package android.net {
method @Nullable public byte[] getWatchlistConfigHash();
}
+ public class PacProxyManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void addPacProxyInstalledListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void removePacProxyInstalledListener(@NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setCurrentProxyScriptUrl(@Nullable android.net.ProxyInfo);
+ }
+
+ public static interface PacProxyManager.PacProxyInstalledListener {
+ method public void onPacProxyInstalled(@Nullable android.net.Network, @NonNull android.net.ProxyInfo);
+ }
+
public final class Proxy {
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
}
@@ -213,6 +251,13 @@ package android.net {
field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
}
+ public class VpnManager {
+ field @Deprecated public static final int TYPE_VPN_LEGACY = 3; // 0x3
+ field public static final int TYPE_VPN_NONE = -1; // 0xffffffff
+ field public static final int TYPE_VPN_PLATFORM = 2; // 0x2
+ field public static final int TYPE_VPN_SERVICE = 1; // 0x1
+ }
+
}
package android.os {
@@ -233,6 +278,10 @@ package android.os {
method public default int getStability();
}
+ public class Process {
+ field public static final int VPN_UID = 1016; // 0x3f8
+ }
+
public class StatsServiceManager {
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 56241bf9e9a7..831ac6f0f107 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -136,6 +136,7 @@ package android {
field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+ field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
@@ -147,6 +148,7 @@ package android {
field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
+ field public static final String MANAGE_SPEECH_RECOGNITION = "android.permission.MANAGE_SPEECH_RECOGNITION";
field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS";
field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
@@ -359,6 +361,7 @@ package android {
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemShell = 17039402; // 0x104002a
field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e
+ field public static final int config_systemTelevisionNotificationHandler = 17039409; // 0x1040031
field public static final int config_systemWellbeing = 17039408; // 0x1040030
field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
}
@@ -380,7 +383,7 @@ package android.accounts {
package android.app {
- public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public void convertFromTranslucent();
method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions);
method @Deprecated public boolean isBackgroundVisibleBehind();
@@ -405,7 +408,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean startProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean stopProfile(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean switchUser(@NonNull android.os.UserHandle);
}
public static interface ActivityManager.OnUidImportanceListener {
@@ -1693,6 +1696,13 @@ package android.app.smartspace {
package android.app.time {
+ public final class Capabilities {
+ field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14
+ field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e
+ field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa
+ field public static final int CAPABILITY_POSSESSED = 40; // 0x28
+ }
+
public final class TimeManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig();
@@ -1709,10 +1719,6 @@ package android.app.time {
method public int getConfigureAutoDetectionEnabledCapability();
method public int getConfigureGeoDetectionEnabledCapability();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14
- field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e
- field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa
- field public static final int CAPABILITY_POSSESSED = 40; // 0x28
field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR;
}
@@ -1793,6 +1799,7 @@ package android.app.usage {
public final class UsageStats implements android.os.Parcelable {
method public int getAppLaunchCount();
+ method public long getLastTimeComponentUsed();
}
public final class UsageStatsManager {
@@ -1886,6 +1893,7 @@ package android.bluetooth {
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
@@ -2062,6 +2070,55 @@ package android.bluetooth {
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
}
+ public final class OobData implements android.os.Parcelable {
+ method @NonNull public static android.bluetooth.OobData.ClassicBuilder createClassicBuilder(@NonNull byte[], @NonNull byte[], @NonNull byte[]);
+ method @NonNull public static android.bluetooth.OobData.LeBuilder createLeBuilder(@NonNull byte[], @NonNull byte[], int);
+ method @NonNull public byte[] getClassOfDevice();
+ method @NonNull public byte[] getClassicLength();
+ method @NonNull public byte[] getConfirmationHash();
+ method @NonNull public byte[] getDeviceAddressWithType();
+ method @Nullable public byte[] getDeviceName();
+ method @Nullable public byte[] getLeAppearance();
+ method @NonNull public int getLeDeviceRole();
+ method @NonNull public int getLeFlags();
+ method @Nullable public byte[] getLeTemporaryKey();
+ method @NonNull public byte[] getRandomizerHash();
+ field public static final int CLASS_OF_DEVICE_OCTETS = 3; // 0x3
+ field public static final int CONFIRMATION_OCTETS = 16; // 0x10
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+ field public static final int DEVICE_ADDRESS_OCTETS = 7; // 0x7
+ field public static final int LE_APPEARANCE_OCTETS = 2; // 0x2
+ field public static final int LE_DEVICE_FLAG_OCTETS = 1; // 0x1
+ field public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 3; // 0x3
+ field public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 2; // 0x2
+ field public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 1; // 0x1
+ field public static final int LE_DEVICE_ROLE_OCTETS = 1; // 0x1
+ field public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0; // 0x0
+ field public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 1; // 0x1
+ field public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0; // 0x0
+ field public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 3; // 0x3
+ field public static final int LE_FLAG_SIMULTANEOUS_HOST = 4; // 0x4
+ field public static final int LE_TK_OCTETS = 16; // 0x10
+ field public static final int OOB_LENGTH_OCTETS = 2; // 0x2
+ field public static final int RANDOMIZER_OCTETS = 16; // 0x10
+ }
+
+ public static final class OobData.ClassicBuilder {
+ method @NonNull public android.bluetooth.OobData build();
+ method @NonNull public android.bluetooth.OobData.ClassicBuilder setClassOfDevice(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.ClassicBuilder setDeviceName(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.ClassicBuilder setRandomizerHash(@NonNull byte[]);
+ }
+
+ public static final class OobData.LeBuilder {
+ method @NonNull public android.bluetooth.OobData build();
+ method @NonNull public android.bluetooth.OobData.LeBuilder setDeviceName(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.LeBuilder setLeFlags(int);
+ method @NonNull public android.bluetooth.OobData.LeBuilder setLeTemporaryKey(@NonNull byte[]);
+ method @NonNull public android.bluetooth.OobData.LeBuilder setRandomizerHash(@NonNull byte[]);
+ }
+
}
package android.bluetooth.le {
@@ -2160,7 +2217,6 @@ package android.content {
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
- field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String FONT_SERVICE = "font";
@@ -2238,6 +2294,7 @@ package android.content {
field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE";
field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
+ field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_HISTORY = "android.intent.action.REVIEW_PERMISSION_HISTORY";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
@@ -2784,21 +2841,29 @@ package android.content.pm.verify.domain {
method @NonNull public String getPackageName();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
+ field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
+ field public static final int STATE_MODIFIABLE_UNVERIFIED = 3; // 0x3
+ field public static final int STATE_MODIFIABLE_VERIFIED = 4; // 0x4
+ field public static final int STATE_NO_RESPONSE = 0; // 0x0
+ field public static final int STATE_SUCCESS = 1; // 0x1
+ field public static final int STATE_UNMODIFIABLE = 2; // 0x2
}
public final class DomainVerificationManager {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
- method public static boolean isStateModifiable(int);
- method public static boolean isStateVerified(int);
method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
- method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public int setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public int setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ field public static final int ERROR_DOMAIN_SET_ID_INVALID = 1; // 0x1
+ field public static final int ERROR_DOMAIN_SET_ID_NULL = 2; // 0x2
+ field public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3; // 0x3
+ field public static final int ERROR_INVALID_STATE_CODE = 6; // 0x6
+ field public static final int ERROR_UNABLE_TO_APPROVE = 5; // 0x5
+ field public static final int ERROR_UNKNOWN_DOMAIN = 4; // 0x4
field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
- field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
- field public static final int STATE_NO_RESPONSE = 0; // 0x0
- field public static final int STATE_SUCCESS = 1; // 0x1
+ field public static final int STATUS_OK = 0; // 0x0
}
public final class DomainVerificationRequest implements android.os.Parcelable {
@@ -2881,11 +2946,16 @@ package android.graphics.fonts {
}
public static final class FontFamilyUpdateRequest.FontFamily {
- ctor public FontFamilyUpdateRequest.FontFamily(@NonNull String, @NonNull java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font>);
method @NonNull public java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font> getFonts();
method @NonNull public String getName();
}
+ public static final class FontFamilyUpdateRequest.FontFamily.Builder {
+ ctor public FontFamilyUpdateRequest.FontFamily.Builder(@NonNull String, @NonNull java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font>);
+ method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.FontFamily.Builder addFont(@NonNull android.graphics.fonts.FontFamilyUpdateRequest.Font);
+ method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.FontFamily build();
+ }
+
public final class FontFileUpdateRequest {
ctor public FontFileUpdateRequest(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[]);
method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor();
@@ -2893,7 +2963,7 @@ package android.graphics.fonts {
}
public class FontManager {
- method @NonNull public android.text.FontConfig getFontConfig();
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig();
method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
@@ -3014,6 +3084,7 @@ package android.hardware.display {
field public final float reduceBrightColorsOffset;
field public final int reduceBrightColorsStrength;
field public final long timeStamp;
+ field @NonNull public final String uniqueDisplayId;
}
public final class BrightnessConfiguration implements android.os.Parcelable {
@@ -3102,6 +3173,7 @@ package android.hardware.hdmi {
method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecEnabled();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecVersion();
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecVolumeControlEnabled();
method public int getPhysicalAddress();
method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
@@ -3109,6 +3181,8 @@ package android.hardware.hdmi {
method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvSendStandbyOnSleep();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvWakeOnOneTouchPlay();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.String> getUserCecSettings();
method public boolean isDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method public void powerOffDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
@@ -3117,10 +3191,13 @@ package android.hardware.hdmi {
method public void setActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecEnabled(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVersion(@NonNull int);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvSendStandbyOnSleep(@NonNull int);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvWakeOnOneTouchPlay(@NonNull int);
field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
field public static final int AVR_VOLUME_MUTED = 101; // 0x65
field public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
@@ -3128,6 +3205,9 @@ package android.hardware.hdmi {
field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "send_standby_on_sleep";
field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
+ field public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
+ field public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = "tv_wake_on_one_touch_play";
+ field public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE = "volume_control_enabled";
field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
field public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 160; // 0xa0
field public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 161; // 0xa1
@@ -3223,6 +3303,12 @@ package android.hardware.hdmi {
field public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 11; // 0xb
field public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 9; // 0x9
field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
+ field public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0; // 0x0
+ field public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1; // 0x1
+ field public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED = 0; // 0x0
+ field public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED = 1; // 0x1
+ field public static final int VOLUME_CONTROL_DISABLED = 0; // 0x0
+ field public static final int VOLUME_CONTROL_ENABLED = 1; // 0x1
}
public static interface HdmiControlManager.CecSettingChangeListener {
@@ -4976,8 +5062,8 @@ package android.media {
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -5123,6 +5209,21 @@ package android.media {
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
}
+ public final class MediaRouter2 {
+ method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
+ method @Nullable public String getClientPackageName();
+ method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
+ method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
+ method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
+ method public void startScan();
+ method public void stopScan();
+ method public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
+ }
+
+ public abstract static class MediaRouter2.RouteCallback {
+ method public void onPreferredFeaturesChanged(@NonNull java.util.List<java.lang.String>);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -7462,22 +7563,22 @@ package android.net.netstats.provider {
package android.net.sip {
- public class SipAudioCall {
- method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
- method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
+ @Deprecated public class SipAudioCall {
+ method @Deprecated @Nullable public android.net.rtp.AudioGroup getAudioGroup();
+ method @Deprecated public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
}
- public class SipManager {
- method @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
- field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
- field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
- field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
- field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
- field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
+ @Deprecated public class SipManager {
+ method @Deprecated @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
+ field @Deprecated public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
+ field @Deprecated public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
+ field @Deprecated public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
+ field @Deprecated public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
+ field @Deprecated public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
}
- public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
- method public int getCallingUid();
+ @Deprecated public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+ method @Deprecated public int getCallingUid();
}
}
@@ -7498,12 +7599,12 @@ package android.net.util {
package android.net.vcn {
public class VcnManager {
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
- method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
}
- public static interface VcnManager.VcnNetworkPolicyListener {
+ public static interface VcnManager.VcnNetworkPolicyChangeListener {
method public void onPolicyChanged();
}
@@ -8152,6 +8253,26 @@ package android.os {
field @NonNull public static final android.os.Parcelable.Creator<android.os.ParcelableHolder> CREATOR;
}
+ public class PowerExemptionManager {
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void addToTemporaryAllowList(@NonNull String, long, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long addToTemporaryAllowListForEvent(@NonNull String, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromAllowList(@NonNull String);
+ field public static final int EVENT_MMS = 2; // 0x2
+ field public static final int EVENT_SMS = 1; // 0x1
+ field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+ field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
+ field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+ field public static final int REASON_GEOFENCING = 100; // 0x64
+ field public static final int REASON_OTHER = 1; // 0x1
+ field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+ field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+ field public static final int REASON_UNKNOWN = 0; // 0x0
+ field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+ field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+ }
+
public final class PowerManager {
method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
@@ -8182,25 +8303,25 @@ package android.os {
field public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1; // 0x1
}
- public class PowerWhitelistManager {
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
+ @Deprecated public class PowerWhitelistManager {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
- field public static final int EVENT_MMS = 2; // 0x2
- field public static final int EVENT_SMS = 1; // 0x1
- field public static final int EVENT_UNSPECIFIED = 0; // 0x0
- field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
- field public static final int REASON_GEOFENCING = 100; // 0x64
- field public static final int REASON_OTHER = 1; // 0x1
- field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
- field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
- field public static final int REASON_UNKNOWN = 0; // 0x0
- field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
- field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
+ field @Deprecated public static final int EVENT_MMS = 2; // 0x2
+ field @Deprecated public static final int EVENT_SMS = 1; // 0x1
+ field @Deprecated public static final int EVENT_UNSPECIFIED = 0; // 0x0
+ field @Deprecated public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+ field @Deprecated public static final int REASON_GEOFENCING = 100; // 0x64
+ field @Deprecated public static final int REASON_OTHER = 1; // 0x1
+ field @Deprecated public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+ field @Deprecated public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+ field @Deprecated public static final int REASON_UNKNOWN = 0; // 0x0
+ field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+ field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class RecoverySystem {
@@ -9587,10 +9708,28 @@ package android.service.dataloader {
package android.service.displayhash {
+ public final class DisplayHashParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.util.Size getBufferSize();
+ method public boolean isBufferScaleWithFiltering();
+ method public boolean isGrayscaleBuffer();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
+ }
+
+ public static final class DisplayHashParams.Builder {
+ ctor public DisplayHashParams.Builder();
+ method @NonNull public android.service.displayhash.DisplayHashParams build();
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferScaleWithFiltering(boolean);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
+ }
+
public abstract class DisplayHasherService extends android.app.Service {
ctor public DisplayHasherService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Nullable public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
+ method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
}
@@ -9931,11 +10070,13 @@ package android.service.resumeonreboot {
package android.service.rotationresolver {
public final class RotationResolutionRequest implements android.os.Parcelable {
+ ctor public RotationResolutionRequest(@NonNull String, int, int, boolean, long);
method public int describeContents();
method public int getCurrentRotation();
method @NonNull public String getPackageName();
method public int getProposedRotation();
method public long getTimeoutMillis();
+ method public boolean shouldUseCamera();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.rotationresolver.RotationResolutionRequest> CREATOR;
}
@@ -10207,6 +10348,7 @@ package android.service.voice {
ctor public HotwordDetectionService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public void onDetectFromDspSource(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback);
+ method public void onUpdateState(@Nullable android.os.Bundle, @Nullable android.os.SharedMemory);
field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
}
@@ -10217,10 +10359,8 @@ package android.service.voice {
public class VoiceInteractionService extends android.app.Service {
method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.Bundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback);
method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
- method public final int setHotwordDetectionConfig(@Nullable android.os.Bundle);
- field public static final int HOTWORD_CONFIG_FAILURE = 1; // 0x1
- field public static final int HOTWORD_CONFIG_SUCCESS = 0; // 0x0
}
}
@@ -10320,14 +10460,36 @@ package android.telecom {
public abstract class CallDiagnosticService extends android.app.Service {
ctor public CallDiagnosticService();
+ method @NonNull public java.util.concurrent.Executor getExecutor();
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport);
method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState);
- method @NonNull public abstract android.telecom.DiagnosticCall onInitializeDiagnosticCall(@NonNull android.telecom.Call.Details);
- method public abstract void onRemoveDiagnosticCall(@NonNull android.telecom.DiagnosticCall);
+ method @NonNull public abstract android.telecom.CallDiagnostics onInitializeCallDiagnostics(@NonNull android.telecom.Call.Details);
+ method public abstract void onRemoveCallDiagnostics(@NonNull android.telecom.CallDiagnostics);
field public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService";
}
+ public abstract class CallDiagnostics {
+ ctor public CallDiagnostics();
+ method public final void clearDiagnosticMessage(int);
+ method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
+ method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
+ method @Nullable public abstract CharSequence onCallDisconnected(int, int);
+ method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
+ method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
+ method public abstract void onReceiveDeviceToDeviceMessage(int, int);
+ method public final void sendDeviceToDeviceMessage(int, int);
+ field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
+ field public static final int BATTERY_STATE_GOOD = 2; // 0x2
+ field public static final int BATTERY_STATE_LOW = 1; // 0x1
+ field public static final int COVERAGE_GOOD = 2; // 0x2
+ field public static final int COVERAGE_POOR = 1; // 0x1
+ field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2
+ field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
+ field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
+ field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
+ }
+
public static class CallScreeningService.CallResponse.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean);
}
@@ -10388,32 +10550,8 @@ package android.telecom {
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public abstract class DiagnosticCall {
- ctor public DiagnosticCall();
- method public final void clearDiagnosticMessage(int);
- method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
- method @NonNull public android.telecom.Call.Details getCallDetails();
- method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
- method @Nullable public abstract CharSequence onCallDisconnected(int, int);
- method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
- method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
- method public abstract void onReceiveDeviceToDeviceMessage(int, int);
- method public final void sendDeviceToDeviceMessage(int, int);
- field public static final int AUDIO_CODEC_AMR_NB = 3; // 0x3
- field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2
- field public static final int AUDIO_CODEC_EVS = 1; // 0x1
- field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
- field public static final int BATTERY_STATE_GOOD = 2; // 0x2
- field public static final int BATTERY_STATE_LOW = 1; // 0x1
- field public static final int COVERAGE_GOOD = 2; // 0x2
- field public static final int COVERAGE_POOR = 1; // 0x1
- field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2
- field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
- field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
- field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
- field public static final int NETWORK_TYPE_IWLAN = 2; // 0x2
- field public static final int NETWORK_TYPE_LTE = 1; // 0x1
- field public static final int NETWORK_TYPE_NR = 3; // 0x3
+ @Deprecated public abstract class DiagnosticCall extends android.telecom.CallDiagnostics {
+ ctor @Deprecated public DiagnosticCall();
}
public abstract class InCallService extends android.app.Service {
@@ -10746,7 +10884,14 @@ package android.telephony {
method @NonNull public static android.os.PersistableBundle getDefaultConfig();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String);
+ field public static final int GBA_DIGEST = 3; // 0x3
+ field public static final int GBA_ME = 1; // 0x1
+ field public static final int GBA_U = 2; // 0x2
field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
+ field public static final String KEY_GBA_MODE_INT = "gba_mode_int";
+ field public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = "gba_ua_security_organization_int";
+ field public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int";
+ field public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int";
field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
}
@@ -10904,6 +11049,19 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ImsiEncryptionInfo> CREATOR;
}
+ public final class LinkCapacityEstimate implements android.os.Parcelable {
+ ctor public LinkCapacityEstimate(int, int, int);
+ method public int describeContents();
+ method public int getDownlinkCapacityKbps();
+ method public int getType();
+ method public int getUplinkCapacityKbps();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LinkCapacityEstimate> CREATOR;
+ field public static final int INVALID = -1; // 0xffffffff
+ field public static final int LCE_TYPE_COMBINED = 2; // 0x2
+ field public static final int LCE_TYPE_PRIMARY = 0; // 0x0
+ field public static final int LCE_TYPE_SECONDARY = 1; // 0x1
+ }
+
public final class LteVopsSupportInfo implements android.os.Parcelable {
ctor public LteVopsSupportInfo(int, int);
method public int describeContents();
@@ -11416,6 +11574,7 @@ package android.telephony {
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; // 0x25
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
@@ -11435,7 +11594,7 @@ package android.telephony {
}
public static interface TelephonyCallback.AllowedNetworkTypesListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(int, long);
}
public static interface TelephonyCallback.CallAttributesListener {
@@ -11446,6 +11605,10 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
}
+ public static interface TelephonyCallback.LinkCapacityEstimateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>);
+ }
+
public static interface TelephonyCallback.OutgoingEmergencyCallListener {
method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
}
@@ -11458,10 +11621,6 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
}
- public static interface TelephonyCallback.PhysicalChannelConfigListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
- }
-
public static interface TelephonyCallback.PreciseCallStateListener {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
}
@@ -11517,7 +11676,6 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -11578,7 +11736,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
- method public boolean isNrDualConnectivityEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11619,7 +11777,7 @@ package android.telephony {
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
- method public int setNrDualConnectivityState(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setNrDualConnectivityState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
@@ -11664,6 +11822,7 @@ package android.telephony {
field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+ field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
@@ -11919,7 +12078,7 @@ package android.telephony.data {
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
@@ -12014,7 +12173,6 @@ package android.telephony.data {
}
public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
- method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel);
method public int describeContents();
method public long getGuaranteedDownlinkBitRate();
method public long getGuaranteedUplinkBitRate();
@@ -12997,6 +13155,7 @@ package android.telephony.ims {
method public void onAutoConfigurationErrorReceived(int, @NonNull String);
method public void onConfigurationChanged(@NonNull byte[]);
method public void onConfigurationReset();
+ method public void onPreProvisioningReceived(@NonNull byte[]);
method public void onRemoved();
}
@@ -13466,6 +13625,7 @@ package android.telephony.ims.stub {
method public int getConfigInt(int);
method public String getConfigString(int);
method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
+ method public final void notifyPreProvisioningReceived(@NonNull byte[]);
method public final void notifyProvisionedValueChanged(int, int);
method public final void notifyProvisionedValueChanged(int, String);
method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
@@ -13797,6 +13957,7 @@ package android.util {
package android.uwb {
public final class AngleMeasurement implements android.os.Parcelable {
+ ctor public AngleMeasurement(double, double, double);
method public int describeContents();
method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
@@ -13805,14 +13966,6 @@ package android.uwb {
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
}
- public static final class AngleMeasurement.Builder {
- ctor public AngleMeasurement.Builder();
- method @NonNull public android.uwb.AngleMeasurement build();
- method @NonNull public android.uwb.AngleMeasurement.Builder setConfidenceLevel(double);
- method @NonNull public android.uwb.AngleMeasurement.Builder setErrorRadians(double);
- method @NonNull public android.uwb.AngleMeasurement.Builder setRadians(double);
- }
-
public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.uwb.AngleMeasurement getAltitude();
@@ -13822,10 +13975,9 @@ package android.uwb {
}
public static final class AngleOfArrivalMeasurement.Builder {
- ctor public AngleOfArrivalMeasurement.Builder();
+ ctor public AngleOfArrivalMeasurement.Builder(@NonNull android.uwb.AngleMeasurement);
method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
- method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAzimuth(@NonNull android.uwb.AngleMeasurement);
}
public final class DistanceMeasurement implements android.os.Parcelable {
@@ -13925,7 +14077,7 @@ package android.uwb {
public final class UwbManager {
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
- method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7b5b1989c1e5..3e0094aec6f9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -28,6 +28,7 @@ package android {
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
+ field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE";
field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
@@ -87,7 +88,7 @@ package android.animation {
package android.app {
- public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public void onMovedToDisplay(int, android.content.res.Configuration);
}
@@ -103,7 +104,9 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
+ method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L
field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL
field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf
@@ -414,6 +417,7 @@ package android.app.admin {
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
@@ -698,7 +702,8 @@ package android.content {
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
field public static final String DREAM_SERVICE = "dream";
field public static final String FONT_SERVICE = "font";
- field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
+ field public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
+ field @Deprecated public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
@@ -706,6 +711,14 @@ package android.content {
method public int getDisplayId();
}
+ public class Intent implements java.lang.Cloneable android.os.Parcelable {
+ field public static final String ACTION_USER_STOPPED = "android.intent.action.USER_STOPPED";
+ }
+
+ public class SyncAdapterType implements android.os.Parcelable {
+ method @Nullable public String getPackageName();
+ }
+
}
package android.content.integrity {
@@ -721,6 +734,13 @@ package android.content.pm {
public class ActivityInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
method public static boolean isTranslucentOrFloating(android.content.res.TypedArray);
+ field public static final long FORCE_NON_RESIZE_APP = 181136395L; // 0xacbec0bL
+ field public static final long FORCE_RESIZE_APP = 174042936L; // 0xa5faf38L
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // 0xabf9183L
+ field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
+ field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
}
@@ -938,6 +958,12 @@ package android.graphics {
method public void splitVertically(@NonNull android.graphics.Rect...);
}
+ public class Typeface {
+ method @NonNull public static java.util.Map<java.lang.String,android.graphics.Typeface> deserializeFontMap(@NonNull java.nio.ByteBuffer) throws java.io.IOException;
+ method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
+ method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
+ }
+
}
package android.graphics.drawable {
@@ -960,7 +986,7 @@ package android.graphics.drawable {
package android.graphics.fonts {
public class FontManager {
- method @NonNull public android.text.FontConfig getFontConfig();
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig();
method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
@@ -1176,7 +1202,7 @@ package android.hardware.soundtrigger {
package android.inputmethodservice {
- public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
+ @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L
}
@@ -1309,6 +1335,7 @@ package android.media {
public class AudioManager {
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method public boolean hasRegisteredDynamicPolicy();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
}
public static final class AudioRecord.MetricsConstants {
@@ -1366,10 +1393,6 @@ package android.media {
method public int getMaxMacroBlocks();
}
- public final class MediaDrm implements java.lang.AutoCloseable {
- method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
- }
-
public final class MediaRoute2Info implements android.os.Parcelable {
method @NonNull public String getOriginalId();
}
@@ -1483,6 +1506,14 @@ package android.net {
package android.os {
+ public final class BatteryStatsManager {
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void resetBattery(boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryLevel(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setChargerAcOnline(boolean, boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void suspendBatteryInput();
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void unplugBattery(boolean);
+ }
+
public class Build {
method public static boolean is64BitAbi(String);
field public static final boolean IS_EMULATOR;
@@ -1928,8 +1959,7 @@ package android.provider {
package android.security {
public final class KeyChain {
- method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") public static boolean removeCredentialManagementApp(@NonNull android.content.Context);
- method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") public static boolean setCredentialManagementApp(@NonNull android.content.Context, @NonNull String, @NonNull android.security.AppUriAuthenticationPolicy);
+ method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") @WorkerThread public static boolean setCredentialManagementApp(@NonNull android.content.Context, @NonNull String, @NonNull android.security.AppUriAuthenticationPolicy);
}
public class KeyStoreException extends java.lang.Exception {
@@ -2123,6 +2153,14 @@ package android.service.watchdog {
}
+package android.speech {
+
+ public class SpeechRecognizer {
+ method public void setTemporaryOnDeviceRecognizer(@Nullable android.content.ComponentName);
+ }
+
+}
+
package android.telecom {
public static class Call.Details {
@@ -2148,6 +2186,10 @@ package android.telecom {
method @NonNull public android.telecom.ConnectionRequest.Builder setVideoState(int);
}
+ public abstract class ConnectionService extends android.app.Service {
+ method public void onBindClient(@Nullable android.content.Intent);
+ }
+
}
package android.telephony {
@@ -2325,7 +2367,6 @@ package android.util {
field public static final String FFLAG_PREFIX = "sys.fflag.";
field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
- field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
}
@@ -2525,6 +2566,7 @@ package android.view {
method public default int getDisplayImePolicy(int);
method public default void holdLock(android.os.IBinder, int);
method public default void setDisplayImePolicy(int, int);
+ method public default void setForceCrossWindowBlurDisabled(boolean);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
method public default boolean shouldShowSystemDecors(int);
@@ -2702,6 +2744,7 @@ package android.view.inputmethod {
public final class InputMethodManager {
method public int getDisplayId();
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method public boolean isInputMethodPickerShown();
}
@@ -2898,7 +2941,7 @@ package android.window {
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
- method @BinderThread public void removeStartingWindow(int);
+ method @BinderThread public void removeStartingWindow(int, @Nullable android.view.SurfaceControl, @Nullable android.graphics.Rect, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer();
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index dab4a5dc316c..d5368213be08 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -555,6 +555,11 @@ public abstract class AccessibilityService extends Service {
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
+ /**
+ * Action to dismiss the notification shade
+ */
+ public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f5f0b422a69d..86e2723a4dc8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1628,6 +1628,18 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Clear the splash screen view if exist.
+ * @hide
+ */
+ public void detachSplashScreenView() {
+ synchronized (this) {
+ if (mSplashScreenView != null) {
+ mSplashScreenView = null;
+ }
+ }
+ }
+
+ /**
* Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
* the attribute {@link android.R.attr#persistableMode} set to
* <code>persistAcrossReboots</code>.
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index fbabfac706e1..633b986c06c9 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -25,6 +25,7 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Singleton;
import android.view.RemoteAnimationDefinition;
+import android.window.SizeConfigurationBuckets;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -104,12 +105,9 @@ public class ActivityClient {
}
}
- void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+ void reportSizeConfigurations(IBinder token, SizeConfigurationBuckets sizeConfigurations) {
try {
- getActivityClientController().reportSizeConfigurations(token,
- horizontalSizeConfiguration, verticalSizeConfigurations,
- smallestSizeConfigurations);
+ getActivityClientController().reportSizeConfigurations(token, sizeConfigurations);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f905ec86aab7..3fedda36de2d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -31,6 +31,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -4048,7 +4049,8 @@ public class ActivityManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS})
public boolean switchUser(@NonNull UserHandle user) {
if (user == null) {
throw new IllegalArgumentException("UserHandle cannot be null.");
@@ -4144,6 +4146,25 @@ public class ActivityManager {
}
}
+ /**
+ * Stops the given {@code userId}.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ public boolean stopUser(@UserIdInt int userId, boolean force) {
+ if (userId == UserHandle.USER_SYSTEM) {
+ return false;
+ }
+ try {
+ return USER_OP_SUCCESS == getService().stopUser(
+ userId, force, /* callback= */ null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** {@hide} */
public static final int FLAG_OR_STOPPED = 1 << 0;
/** {@hide} */
@@ -4811,6 +4832,21 @@ public class ActivityManager {
}
/**
+ * Blocks until all broadcast queues become idle.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public void waitForBroadcastIdle() {
+ try {
+ getService().waitForBroadcastIdle();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* A subset of immutable pending intent information suitable for caching on the client side.
*
* @hide
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 233f737b8e0f..a24f8716b1cc 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -394,6 +394,20 @@ public class ActivityTaskManager {
}
/**
+ * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+ * if the request orientation is not met, and will be shown in size-compat mode if the container
+ * size has changed.
+ * @hide
+ */
+ public static boolean supportsNonResizableMultiWindow() {
+ try {
+ return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
* @hide
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0b5958695dff..c1d8541311a2 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -158,7 +158,6 @@ import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.SuperNotCalledException;
import android.util.UtilConfig;
import android.util.proto.ProtoOutputStream;
@@ -180,6 +179,7 @@ import android.view.contentcapture.IContentCaptureManager;
import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.translation.TranslationSpec;
import android.webkit.WebView;
+import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
import android.window.SplashScreenView;
@@ -292,7 +292,6 @@ public final class ActivityThread extends ClientTransactionHandler
/** Use background GC policy and default JIT threshold. */
private static final int VM_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1;
- private static final int REMOVE_SPLASH_SCREEN_VIEW_TIMEOUT = 5000;
/**
* Denotes an invalid sequence number corresponding to a process state change.
*/
@@ -604,6 +603,8 @@ public final class ActivityThread extends ClientTransactionHandler
@LifecycleState
private int mLifecycleState = PRE_ON_CREATE;
+ private SizeConfigurationBuckets mSizeConfigurations;
+
@VisibleForTesting
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public ActivityClientRecord() {
@@ -1954,8 +1955,6 @@ public final class ActivityThread extends ClientTransactionHandler
public static final int INSTRUMENT_WITHOUT_RESTART = 170;
public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
- public static final int REMOVE_SPLASH_SCREEN_VIEW = 172;
-
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
@@ -2004,8 +2003,6 @@ public final class ActivityThread extends ClientTransactionHandler
case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
- case REMOVE_SPLASH_SCREEN_VIEW:
- return "REMOVE_SPLASH_SCREEN_VIEW";
}
}
return Integer.toString(code);
@@ -2202,9 +2199,6 @@ public final class ActivityThread extends ClientTransactionHandler
case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
handleFinishInstrumentationWithoutRestart();
break;
- case REMOVE_SPLASH_SCREEN_VIEW:
- handleRemoveSplashScreenView((ActivityClientRecord) msg.obj);
- break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -3764,23 +3758,8 @@ public final class ActivityThread extends ClientTransactionHandler
if (configurations == null) {
return;
}
- SparseIntArray horizontal = new SparseIntArray();
- SparseIntArray vertical = new SparseIntArray();
- SparseIntArray smallest = new SparseIntArray();
- for (int i = configurations.length - 1; i >= 0; i--) {
- Configuration config = configurations[i];
- if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- vertical.put(config.screenHeightDp, 0);
- }
- if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- horizontal.put(config.screenWidthDp, 0);
- }
- if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- smallest.put(config.smallestScreenWidthDp, 0);
- }
- }
- ActivityClient.getInstance().reportSizeConfigurations(r.token, horizontal.copyKeys(),
- vertical.copyKeys(), smallest.copyKeys());
+ r.mSizeConfigurations = new SizeConfigurationBuckets(configurations);
+ ActivityClient.getInstance().reportSizeConfigurations(r.token, r.mSizeConfigurations);
}
private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) {
@@ -4033,6 +4012,7 @@ public final class ActivityThread extends ClientTransactionHandler
view.cacheRootWindow(r.window);
view.makeSystemUIColorsTransparent();
r.activity.mSplashScreenView = view;
+ view.attachHostActivity(r.activity);
view.requestLayout();
// Ensure splash screen view is shown before remove the splash screen window.
final ViewRootImpl impl = decorView.getViewRootImpl();
@@ -4075,8 +4055,6 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public void handOverSplashScreenView(@NonNull ActivityClientRecord r) {
if (r.activity.mSplashScreenView != null) {
- Message msg = mH.obtainMessage(H.REMOVE_SPLASH_SCREEN_VIEW, r);
- mH.sendMessageDelayed(msg, REMOVE_SPLASH_SCREEN_VIEW_TIMEOUT);
synchronized (this) {
if (mSplashScreenGlobal != null) {
mSplashScreenGlobal.dispatchOnExitAnimation(r.token,
@@ -4087,16 +4065,6 @@ public final class ActivityThread extends ClientTransactionHandler
}
/**
- * Force remove splash screen view.
- */
- private void handleRemoveSplashScreenView(@NonNull ActivityClientRecord r) {
- if (r.activity.mSplashScreenView != null) {
- r.activity.mSplashScreenView.remove();
- r.activity.mSplashScreenView = null;
- }
- }
-
- /**
* Cycle activity through onPause and onUserLeaveHint so that PIP is entered if supported, then
* return to its previous state. This allows activities that rely on onUserLeaveHint instead of
* onPictureInPictureRequested to enter picture-in-picture.
@@ -5773,13 +5741,19 @@ public final class ActivityThread extends ClientTransactionHandler
// onConfigurationChanged.
// TODO(b/173090263): Use diff instead after the improvement of AssetManager and
// ResourcesImpl constructions.
- final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
-
- if (diff == 0 && !shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
- && !movedToDifferentDisplay && mResourcesManager.isSameResourcesOverrideConfig(
- activityToken, amOverrideConfig)) {
- // Nothing significant, don't proceed with updating and reporting.
- return null;
+ int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
+ final ActivityClientRecord cr = getActivityClient(activityToken);
+ diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig,
+ cr != null ? cr.mSizeConfigurations : null);
+
+ if (diff == 0) {
+ if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
+ && !movedToDifferentDisplay
+ && mResourcesManager.isSameResourcesOverrideConfig(
+ activityToken, amOverrideConfig)) {
+ // Nothing significant, don't proceed with updating and reporting.
+ return null;
+ }
} else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
// If this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged. Otherwise, report to the activity for the
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dd1bc7c61547..27b19bcd31a1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -56,7 +56,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserManager;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongSparseArray;
@@ -89,6 +89,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -200,9 +201,12 @@ public class AppOpsManager {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L;
+ private static final String FULL_LOG = "privacy_attribution_tag_full_log_enabled";
private static final int MAX_UNFORWARDED_OPS = 10;
+ private static Boolean sFullLog = null;
+
final Context mContext;
@UnsupportedAppUsage
@@ -1203,9 +1207,21 @@ public class AppOpsManager {
*/
public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE;
+ /**
+ * Allow apps to create the requests to manage the media files without user confirmation.
+ *
+ * @see android.Manifest.permission#MANAGE_MEDIA
+ * @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)
+ * @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)
+ * @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)
+ *
+ * @hide
+ */
+ public static final int OP_MANAGE_MEDIA = AppProtoEnums.APP_OP_MANAGE_MEDIA;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 110;
+ public static final int _NUM_OP = 111;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1603,6 +1619,18 @@ public class AppOpsManager {
*/
public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source";
+ /**
+ * Allow apps to create the requests to manage the media files without user confirmation.
+ *
+ * @see android.Manifest.permission#MANAGE_MEDIA
+ * @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)
+ * @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)
+ * @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)
+ *
+ * @hide
+ */
+ public static final String OPSTR_MANAGE_MEDIA = "android:manage_media";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1684,6 +1712,7 @@ public class AppOpsManager {
OP_MANAGE_ONGOING_CALLS,
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
OP_SCHEDULE_EXACT_ALARM,
+ OP_MANAGE_MEDIA,
};
/**
@@ -1805,6 +1834,7 @@ public class AppOpsManager {
OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM
OP_FINE_LOCATION, // OP_FINE_LOCATION_SOURCE
OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE
+ OP_MANAGE_MEDIA, // MANAGE_MEDIA
};
/**
@@ -1921,6 +1951,7 @@ public class AppOpsManager {
OPSTR_SCHEDULE_EXACT_ALARM,
OPSTR_FINE_LOCATION_SOURCE,
OPSTR_COARSE_LOCATION_SOURCE,
+ OPSTR_MANAGE_MEDIA,
};
/**
@@ -2038,6 +2069,7 @@ public class AppOpsManager {
"SCHEDULE_EXACT_ALARM",
"FINE_LOCATION_SOURCE",
"COARSE_LOCATION_SOURCE",
+ "MANAGE_MEDIA",
};
/**
@@ -2156,6 +2188,7 @@ public class AppOpsManager {
Manifest.permission.SCHEDULE_EXACT_ALARM,
null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE,
null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE,
+ Manifest.permission.MANAGE_MEDIA,
};
/**
@@ -2274,6 +2307,7 @@ public class AppOpsManager {
null, // SCHEDULE_EXACT_ALARM
null, // ACCESS_FINE_LOCATION_SOURCE
null, // ACCESS_COARSE_LOCATION_SOURCE
+ null, // MANAGE_MEDIA
};
/**
@@ -2391,6 +2425,7 @@ public class AppOpsManager {
null, // SCHEDULE_EXACT_ALARM
null, // ACCESS_FINE_LOCATION_SOURCE
null, // ACCESS_COARSE_LOCATION_SOURCE
+ null, // MANAGE_MEDIA
};
/**
@@ -2507,6 +2542,7 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE
AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE
+ AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA
};
/**
@@ -2627,6 +2663,7 @@ public class AppOpsManager {
false, // SCHEDULE_EXACT_ALARM
false, // ACCESS_FINE_LOCATION_SOURCE
false, // ACCESS_COARSE_LOCATION_SOURCE
+ false, // MANAGE_MEDIA
};
/**
@@ -6972,6 +7009,26 @@ public class AppOpsManager {
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
+
+ if (mContext != null) {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ if (pm != null && pm.checkPermission(Manifest.permission.READ_DEVICE_CONFIG,
+ mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+ mContext.getMainExecutor(), properties -> {
+ if (properties.getKeyset().contains(FULL_LOG)) {
+ sFullLog = properties.getBoolean(FULL_LOG, false);
+ }
+ });
+ return;
+ }
+ } catch (Exception e) {
+ // This manager was made before DeviceConfig is ready, so it's a low-level
+ // system app. We likely don't care about its logs.
+ }
+ }
+ sFullLog = false;
}
/**
@@ -8056,8 +8113,8 @@ public class AppOpsManager {
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
- myUid) == PackageManager.PERMISSION_GRANTED || isTrustedVoiceServiceProxy(
- mContext, mContext.getOpPackageName(), op, mContext.getUserId()))) {
+ myUid) == PackageManager.PERMISSION_GRANTED ||
+ Binder.getCallingUid() == proxiedUid)) {
collectNotedOpSync(op, proxiedAttributionTag);
}
}
@@ -8068,28 +8125,6 @@ public class AppOpsManager {
}
}
- /**
- * Checks if the voice recognition service is a trust proxy.
- *
- * @return {@code true} if the package is a trust voice recognition service proxy
- * @hide
- */
- public static boolean isTrustedVoiceServiceProxy(Context context, String packageName,
- int code, int userId) {
- // This is a workaround for R QPR, new API change is not allowed. We only allow the current
- // voice recognizer is also the voice interactor to noteproxy op.
- if (code != OP_RECORD_AUDIO) {
- return false;
- }
- final String voiceRecognitionComponent = Settings.Secure.getStringForUser(
- context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE, userId);
-
- final String voiceRecognitionServicePackageName =
- getComponentPackageNameFromString(voiceRecognitionComponent);
- return (Objects.equals(packageName, voiceRecognitionServicePackageName))
- && isPackagePreInstalled(context, packageName, userId);
- }
-
private static String getComponentPackageNameFromString(String from) {
ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null;
return componentName != null ? componentName.getPackageName() : "";
@@ -8464,8 +8499,7 @@ public class AppOpsManager {
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
Process.myUid()) == PackageManager.PERMISSION_GRANTED
- || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), opInt,
- mContext.getUserId()))) {
+ || Binder.getCallingUid() == proxiedUid)) {
collectNotedOpSync(opInt, proxiedAttributionTag);
}
}
@@ -9110,10 +9144,20 @@ public class AppOpsManager {
StringBuilder sb = new StringBuilder();
for (int i = firstInteresting; i <= lastInteresting; i++) {
+ if (sFullLog == null) {
+ try {
+ sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ FULL_LOG, false);
+ } catch (Exception e) {
+ // This should not happen, but it may, in rare cases
+ sFullLog = false;
+ }
+ }
+
if (i != firstInteresting) {
sb.append('\n');
}
- if (sb.length() + trace[i].toString().length() > 600) {
+ if (!sFullLog && sb.length() + trace[i].toString().length() > 600) {
break;
}
sb.append(trace[i]);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a6260d6d9cad..dba62b9d3b63 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3228,6 +3228,12 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void registerDexModule(@NonNull String dexModule,
@Nullable DexModuleRegisterCallback callback) {
+ // Create the callback delegate to be passed to package manager service.
+ DexModuleRegisterCallbackDelegate callbackDelegate = null;
+ if (callback != null) {
+ callbackDelegate = new DexModuleRegisterCallbackDelegate(callback);
+ }
+
// Check if this is a shared module by looking if the others can read it.
boolean isSharedModule = false;
try {
@@ -3236,18 +3242,13 @@ public class ApplicationPackageManager extends PackageManager {
isSharedModule = true;
}
} catch (ErrnoException e) {
- callback.onDexModuleRegistered(dexModule, false,
- "Could not get stat the module file: " + e.getMessage());
+ if (callbackDelegate != null) {
+ callback.onDexModuleRegistered(dexModule, false,
+ "Could not get stat the module file: " + e.getMessage());
+ }
return;
}
- // Module path is ok.
- // Create the callback delegate to be passed to package manager service.
- DexModuleRegisterCallbackDelegate callbackDelegate = null;
- if (callback != null) {
- callbackDelegate = new DexModuleRegisterCallbackDelegate(callback);
- }
-
// Invoke the package manager service.
try {
mPM.registerDexModule(mContext.getPackageName(), dexModule,
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3af07635c185..0358fe56203c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2586,9 +2586,7 @@ class ContextImpl extends Context {
@Override
public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
if (display == null) {
- throw new UnsupportedOperationException("Token context can only be created from "
- + "other visual contexts, such as Activity or one created with "
- + "Context#createDisplayContext(Display)");
+ throw new IllegalArgumentException("Display must not be null");
}
final ContextImpl tokenContext = createBaseWindowContext(token, display);
tokenContext.setResources(createWindowContextResources());
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 573931ed228e..ed4836e31209 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -25,6 +25,7 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.RemoteAnimationDefinition;
+import android.window.SizeConfigurationBuckets;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -49,8 +50,8 @@ interface IActivityClientController {
oneway void activityDestroyed(in IBinder token);
oneway void activityRelaunched(in IBinder token);
- oneway void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
- in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
+ oneway void reportSizeConfigurations(in IBinder token,
+ in SizeConfigurationBuckets sizeConfigurations);
boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index ef0dcabbe111..4c2433c04771 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -709,4 +709,7 @@ interface IActivityManager {
ParceledListSlice queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
int getUidProcessCapabilities(int uid, in String callingPackage);
+
+ /** Blocks until all broadcast queues become idle. */
+ void waitForBroadcastIdle();
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 542f754ce364..3bfddf7db015 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -289,6 +289,13 @@ interface IActivityTaskManager {
void setSplitScreenResizing(boolean resizing);
boolean supportsLocalVoiceInteraction();
+ /**
+ * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+ * if the request orientation is not met, and will be shown in size-compat mode if the container
+ * size has changed.
+ */
+ boolean supportsNonResizableMultiWindow();
+
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5402381b7207..e83557c18f8f 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -167,7 +167,8 @@ interface IWallpaperManager {
* @hide
*/
void removeOnLocalColorsChangedListener(
- in ILocalWallpaperColorConsumer callback, int which, int userId, int displayId);
+ in ILocalWallpaperColorConsumer callback, in List<RectF> area,
+ int which, int userId, int displayId);
/**
* @hide
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index be426aa7ed2b..83d0246744df 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1110,6 +1110,10 @@ public final class LoadedApk {
mPackageName,
PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.myUserId());
+ if (pi == null) {
+ throw new IllegalStateException("Unable to get package info for "
+ + mPackageName + "; is package not installed?");
+ }
/*
* Two possible indications that this package could be
* sharing its virtual machine with other packages:
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b00cfcb0d020..9dcb23711a52 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -823,6 +823,17 @@ public class Notification implements Parcelable
public static final int VISIBILITY_SECRET = -1;
/**
+ * @hide
+ */
+ @IntDef(prefix = "VISIBILITY_", value = {
+ VISIBILITY_PUBLIC,
+ VISIBILITY_PRIVATE,
+ VISIBILITY_SECRET,
+ NotificationManager.VISIBILITY_NO_OVERRIDE
+ })
+ public @interface NotificationVisibilityOverride{};
+
+ /**
* Notification category: incoming call (voice or video) or similar synchronous communication request.
*/
public static final String CATEGORY_CALL = "call";
@@ -1611,7 +1622,8 @@ public class Notification implements Parcelable
/**
* {@code SemanticAction}: Mark the conversation associated with the notification as a
- * priority. Note that this is only for use by the notification assistant services.
+ * priority. Note that this is only for use by the notification assistant services. The
+ * type will be ignored for actions an app adds to its own notifications.
* @hide
*/
@SystemApi
@@ -1619,7 +1631,8 @@ public class Notification implements Parcelable
/**
* {@code SemanticAction}: Mark content as a potential phishing attempt.
- * Note that this is only for use by the notification assistant services.
+ * Note that this is only for use by the notification assistant services. The type will
+ * be ignored for actions an app adds to its own notifications.
* @hide
*/
@SystemApi
@@ -5009,17 +5022,24 @@ public class Notification implements Parcelable
boolean showProgress = handleProgressBar(contentView, ex, p);
boolean hasSecondLine = showProgress;
if (p.hasTitle()) {
- contentView.setViewVisibility(R.id.title, View.VISIBLE);
- contentView.setTextViewText(R.id.title, processTextSpans(p.title));
- setTextViewColorPrimary(contentView, R.id.title, p);
+ contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
+ contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.title));
+ setTextViewColorPrimary(contentView, p.mTitleViewId, p);
+ } else if (p.mTitleViewId != R.id.title) {
+ // This alternate title view ID is not cleared by resetStandardTemplate
+ contentView.setViewVisibility(p.mTitleViewId, View.GONE);
+ contentView.setTextViewText(p.mTitleViewId, null);
}
if (p.text != null && p.text.length() != 0
&& (!showProgress || p.mAllowTextWithProgress)) {
- int textId = com.android.internal.R.id.text;
- contentView.setTextViewText(textId, processTextSpans(p.text));
- setTextViewColorSecondary(contentView, textId, p);
- contentView.setViewVisibility(textId, View.VISIBLE);
+ contentView.setViewVisibility(p.mTextViewId, View.VISIBLE);
+ contentView.setTextViewText(p.mTextViewId, processTextSpans(p.text));
+ setTextViewColorSecondary(contentView, p.mTextViewId, p);
hasSecondLine = true;
+ } else if (p.mTextViewId != R.id.text) {
+ // This alternate text view ID is not cleared by resetStandardTemplate
+ contentView.setViewVisibility(p.mTextViewId, View.GONE);
+ contentView.setTextViewText(p.mTextViewId, null);
}
setHeaderlessVerticalMargins(contentView, p, hasSecondLine);
@@ -5211,6 +5231,9 @@ public class Notification implements Parcelable
// views in states with a header (big states)
result.mHeadingExtraMarginSet.applyToView(contentView, R.id.notification_header);
result.mTitleMarginSet.applyToView(contentView, R.id.title);
+ // If there is no title, the text (or big_text) needs to wrap around the image
+ result.mTitleMarginSet.applyToView(contentView, p.mTextViewId);
+ contentView.setInt(p.mTextViewId, "setNumIndentLines", p.hasTitle() ? 0 : 1);
}
}
@@ -5299,7 +5322,7 @@ public class Notification implements Parcelable
contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor);
contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
// Use different highlighted colors except when low-priority mode prevents that
- if (!p.forceDefaultColor) {
+ if (!p.mReduceHighlights) {
textColor = getBackgroundColor(p);
pillColor = getAccentColor(p);
}
@@ -5433,6 +5456,11 @@ public class Notification implements Parcelable
// keep the divider visible between that title and the next text element.
return true;
}
+ if (p.mHideAppName) {
+ // The app name is being hidden, so we definitely want to return here.
+ // Assume that there is a title which will replace it in the header.
+ return p.hasTitle();
+ }
contentView.setViewVisibility(R.id.app_name_text, View.VISIBLE);
contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
@@ -5443,8 +5471,12 @@ public class Notification implements Parcelable
return p.allowColorization && mN.isColorized();
}
- private boolean isCallActionColorCustomizable(StandardTemplateParams p) {
- return isColorized(p) && mContext.getResources().getBoolean(
+ private boolean isCallActionColorCustomizable() {
+ // NOTE: this doesn't need to check StandardTemplateParams.allowColorization because
+ // that is only used for disallowing colorization of headers for the minimized state,
+ // and neither of those conditions applies when showing actions.
+ // Not requiring StandardTemplateParams as an argument simplifies the creation process.
+ return mN.isColorized() && mContext.getResources().getBoolean(
R.bool.config_callNotificationActionColorsRequireColorized);
}
@@ -5510,13 +5542,13 @@ public class Notification implements Parcelable
*/
private @NonNull List<Notification.Action> getNonContextualActions() {
if (mActions == null) return Collections.emptyList();
- List<Notification.Action> contextualActions = new ArrayList<>();
+ List<Notification.Action> standardActions = new ArrayList<>();
for (Notification.Action action : mActions) {
if (!action.isContextual()) {
- contextualActions.add(action);
+ standardActions.add(action);
}
}
- return contextualActions;
+ return standardActions;
}
private RemoteViews applyStandardTemplateWithActions(int layoutId,
@@ -5536,16 +5568,29 @@ public class Notification implements Parcelable
// filter them out here.
List<Notification.Action> nonContextualActions = getNonContextualActions();
- int N = nonContextualActions.size();
- boolean emphazisedMode = mN.fullScreenIntent != null;
+ int numActions = Math.min(nonContextualActions.size(), MAX_ACTION_BUTTONS);
+ boolean emphazisedMode = mN.fullScreenIntent != null || p.mCallStyleActions;
+ if (p.mCallStyleActions) {
+ // Clear view padding to allow buttons to start on the left edge.
+ // This must be done before 'setEmphasizedMode' which sets top/bottom margins.
+ big.setViewPadding(R.id.actions, 0, 0, 0, 0);
+ // Add an optional indent that will make buttons start at the correct column when
+ // there is enough space to do so (and fall back to the left edge if not).
+ big.setInt(R.id.actions, "setCollapsibleIndentDimen",
+ R.dimen.call_notification_collapsible_indent);
+ }
big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
- if (N > 0 && !p.mHideActions) {
+ if (p.mCallStyleActions) {
+ // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
+ // required actions (Answer, Decline, and Hang Up).
+ big.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
+ }
+ if (numActions > 0 && !p.mHideActions) {
big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
RemoteViews.MARGIN_BOTTOM, 0);
- if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < numActions; i++) {
Action action = nonContextualActions.get(i);
boolean actionHasValidInput = hasValidRemoteInput(action);
@@ -5556,6 +5601,11 @@ public class Notification implements Parcelable
// Clear the drawable
button.setInt(R.id.action0, "setBackgroundResource", 0);
}
+ if (p.mCallStyleActions && i > 0) {
+ // Clear start margin from non-first buttons to reduce the gap between them.
+ // (8dp remaining gap is from all buttons' standard 4dp inset).
+ button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
+ }
big.addView(R.id.actions, button);
}
} else {
@@ -5773,11 +5823,11 @@ public class Notification implements Parcelable
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
return true;
}
- // If the big content view has no content, we can exempt the app from having to show it.
+ // Notifications with contentView and without a bigContentView, style, or actions would
+ // not have an expanded state before S, so showing the standard template expanded state
+ // usually looks wrong, so we keep it simple and don't show the expanded state.
boolean exempt = mN.contentView != null && mN.bigContentView == null
- && mStyle == null && mActions.size() == 0
- && mN.extras.getCharSequence(EXTRA_TITLE) == null
- && mN.extras.getCharSequence(EXTRA_TEXT) == null;
+ && mStyle == null && mActions.size() == 0;
return !exempt;
}
@@ -5787,7 +5837,7 @@ public class Notification implements Parcelable
*
* @hide
*/
- public RemoteViews makeNotificationHeader() {
+ public RemoteViews makeNotificationGroupHeader() {
return makeNotificationHeader(mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_GROUP_HEADER)
.fillTextsFrom(this));
@@ -5913,7 +5963,7 @@ public class Notification implements Parcelable
.viewType(StandardTemplateParams.VIEW_TYPE_PUBLIC)
.fillTextsFrom(this);
if (isLowPriority) {
- params.forceDefaultColor();
+ params.reduceHighlights();
}
view = makeNotificationHeader(params);
view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
@@ -5936,7 +5986,7 @@ public class Notification implements Parcelable
public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
StandardTemplateParams p = mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
- .forceDefaultColor()
+ .reduceHighlights()
.fillTextsFrom(this);
if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
p.summaryText(createSummaryText());
@@ -6017,7 +6067,7 @@ public class Notification implements Parcelable
button.setColorStateList(R.id.action0, "setButtonBackground",
ColorStateList.valueOf(background));
button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
- if (p.mAllowActionIcons) {
+ if (p.mCallStyleActions) {
button.setImageViewIcon(R.id.action0, action.getIcon());
boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
button.setBoolean(R.id.action0, "setWrapModePriority", priority);
@@ -6276,7 +6326,9 @@ public class Notification implements Parcelable
* @param p the template params to inflate this with
*/
private @ColorInt int getRawColor(StandardTemplateParams p) {
- if (p.forceDefaultColor) {
+ // When notifications are theme-tinted, the raw color is only used for the icon, so go
+ // ahead and keep that color instead of changing the color for minimized notifs.
+ if (p.mReduceHighlights && !mTintWithThemeAccent) {
return COLOR_DEFAULT;
}
return mN.color;
@@ -6553,10 +6605,6 @@ public class Notification implements Parcelable
return R.layout.notification_template_material_conversation;
}
- private int getCallLayoutResource() {
- return R.layout.notification_template_material_call;
- }
-
private int getActionLayoutResource() {
return R.layout.notification_material_action;
}
@@ -7014,16 +7062,7 @@ public class Notification implements Parcelable
p.title = mBigContentTitle;
}
- RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, p,
- result);
-
- if (mBigContentTitle != null && mBigContentTitle.equals("")) {
- contentView.setViewVisibility(R.id.title, View.GONE);
- } else {
- contentView.setViewVisibility(R.id.title, View.VISIBLE);
- }
-
- return contentView;
+ return mBuilder.applyStandardTemplateWithActions(layoutId, p, result);
}
/**
@@ -7616,22 +7655,16 @@ public class Notification implements Parcelable
public RemoteViews makeBigContentView() {
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_BIG)
- .fillTextsFrom(mBuilder).text(null);
- RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), p, null);
+ .textViewId(R.id.big_text)
+ .fillTextsFrom(mBuilder);
+ // Replace the text with the big text, but only if the big text is not empty.
CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
- if (TextUtils.isEmpty(bigTextText)) {
- // In case the bigtext is null / empty fall back to the normal text to avoid a weird
- // experience
- bigTextText = mBuilder.processLegacyText(
- mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT));
+ if (!TextUtils.isEmpty(bigTextText)) {
+ p.text(bigTextText);
}
- contentView.setTextViewText(R.id.big_text, mBuilder.processTextSpans(bigTextText));
- mBuilder.setTextViewColorSecondary(contentView, R.id.big_text, p);
- contentView.setViewVisibility(R.id.big_text,
- TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
- return contentView;
+ return getStandardView(mBuilder.getBigTextLayoutResource(), p, null /* result */);
}
/**
@@ -9263,6 +9296,17 @@ public class Notification implements Parcelable
return this;
}
+ /** @hide */
+ @Override
+ public Notification buildStyled(Notification wip) {
+ wip = super.buildStyled(wip);
+ // ensure that the actions in the builder and notification are corrected.
+ mBuilder.mActions = getActionsListWithSystemActions();
+ wip.actions = new Action[mBuilder.mActions.size()];
+ mBuilder.mActions.toArray(wip.actions);
+ return wip;
+ }
+
/**
* @hide
*/
@@ -9303,7 +9347,7 @@ public class Notification implements Parcelable
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- return makeCallLayout();
+ return makeCallLayout(StandardTemplateParams.VIEW_TYPE_NORMAL);
}
/**
@@ -9311,25 +9355,25 @@ public class Notification implements Parcelable
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- return makeCallLayout();
+ return makeCallLayout(StandardTemplateParams.VIEW_TYPE_HEADS_UP);
}
/**
* @hide
*/
public RemoteViews makeBigContentView() {
- return makeCallLayout();
+ return makeCallLayout(StandardTemplateParams.VIEW_TYPE_BIG);
}
@NonNull
- private Action makeNegativeAction(@NonNull StandardTemplateParams p) {
+ private Action makeNegativeAction() {
if (mDeclineIntent == null) {
- return makeAction(p, R.drawable.ic_call_decline,
+ return makeAction(R.drawable.ic_call_decline,
R.string.call_notification_hang_up_action,
mDeclineButtonColor, R.color.call_notification_decline_color,
mHangUpIntent);
} else {
- return makeAction(p, R.drawable.ic_call_decline,
+ return makeAction(R.drawable.ic_call_decline,
R.string.call_notification_decline_action,
mDeclineButtonColor, R.color.call_notification_decline_color,
mDeclineIntent);
@@ -9337,18 +9381,17 @@ public class Notification implements Parcelable
}
@Nullable
- private Action makeAnswerAction(@NonNull StandardTemplateParams p) {
- return mAnswerIntent == null ? null : makeAction(p, R.drawable.ic_call_answer,
+ private Action makeAnswerAction() {
+ return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
R.string.call_notification_answer_action,
mAnswerButtonColor, R.color.call_notification_answer_color,
mAnswerIntent);
}
@NonNull
- private Action makeAction(@NonNull StandardTemplateParams p,
- @DrawableRes int icon, @StringRes int title,
+ private Action makeAction(@DrawableRes int icon, @StringRes int title,
@ColorInt Integer colorInt, @ColorRes int defaultColorRes, PendingIntent intent) {
- if (colorInt == null || !mBuilder.isCallActionColorCustomizable(p)) {
+ if (colorInt == null || !mBuilder.isCallActionColorCustomizable()) {
colorInt = mBuilder.mContext.getColor(defaultColorRes);
}
Action action = new Action.Builder(Icon.createWithResource("", icon),
@@ -9360,33 +9403,68 @@ public class Notification implements Parcelable
return action;
}
- private ArrayList<Action> makeActionsList(@NonNull StandardTemplateParams p) {
- final Action negativeAction = makeNegativeAction(p);
- final Action answerAction = makeAnswerAction(p);
+ private boolean isActionAddedByCallStyle(Action action) {
+ // This is an internal extra added by the style to these actions. If an app were to add
+ // this extra to the action themselves, the action would be dropped. :shrug:
+ return action != null && action.getExtras().getBoolean(KEY_ACTION_PRIORITY);
+ }
- ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS);
- final Action lastAction;
- if (answerAction == null) {
- // If there's no answer action, put the hang up / decline action at the end
- lastAction = negativeAction;
- } else {
- // Otherwise put the answer action at the end, and put the decline action at start.
- actions.add(negativeAction);
- lastAction = answerAction;
- }
- // For consistency with the standard actions bar, contextual actions are ignored.
- for (Action action : mBuilder.getNonContextualActions()) {
- if (actions.size() >= MAX_ACTION_BUTTONS - 1) {
- break;
+ /**
+ * Gets the actions list for the call with the answer/decline/hangUp actions inserted in
+ * the correct place. This returns the correct result even if the system actions have
+ * already been added, and even if more actions were added since then.
+ * @hide
+ */
+ @NonNull
+ public ArrayList<Action> getActionsListWithSystemActions() {
+ // Define the system actions we expect to see
+ final Action negativeAction = makeNegativeAction();
+ final Action answerAction = makeAnswerAction();
+ // Sort the expected actions into the correct order:
+ // * If there's no answer action, put the hang up / decline action at the end
+ // * Otherwise put the answer action at the end, and put the decline action at start.
+ final Action firstAction = answerAction == null ? null : negativeAction;
+ final Action lastAction = answerAction == null ? negativeAction : answerAction;
+
+ // Start creating the result list.
+ int nonContextualActionSlotsRemaining = MAX_ACTION_BUTTONS;
+ ArrayList<Action> resultActions = new ArrayList<>(MAX_ACTION_BUTTONS);
+ if (firstAction != null) {
+ resultActions.add(firstAction);
+ --nonContextualActionSlotsRemaining;
+ }
+
+ // Copy actions into the new list, correcting system actions.
+ if (mBuilder.mActions != null) {
+ for (Notification.Action action : mBuilder.mActions) {
+ if (action.isContextual()) {
+ // Always include all contextual actions
+ resultActions.add(action);
+ } else if (isActionAddedByCallStyle(action)) {
+ // Drop any old versions of system actions
+ } else {
+ // Copy non-contextual actions; decrement the remaining action slots.
+ resultActions.add(action);
+ --nonContextualActionSlotsRemaining;
+ }
+ // If there's exactly one action slot left, fill it with the lastAction.
+ if (nonContextualActionSlotsRemaining == 1) {
+ resultActions.add(lastAction);
+ --nonContextualActionSlotsRemaining;
+ }
}
- actions.add(action);
}
- actions.add(lastAction);
- return actions;
+ // If there are any action slots left, the lastAction still needs to be added.
+ if (nonContextualActionSlotsRemaining >= 1) {
+ resultActions.add(lastAction);
+ }
+ return resultActions;
}
- private RemoteViews makeCallLayout() {
+ private RemoteViews makeCallLayout(int viewType) {
+ final boolean isCollapsed = viewType == StandardTemplateParams.VIEW_TYPE_NORMAL;
Bundle extras = mBuilder.mN.extras;
+ CharSequence title = mPerson != null ? mPerson.getName() : null;
CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
if (text == null) {
text = getDefaultText();
@@ -9394,24 +9472,30 @@ public class Notification implements Parcelable
// Bind standard template
StandardTemplateParams p = mBuilder.mParams.reset()
- .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
- .allowActionIcons(true)
+ .viewType(viewType)
+ .callStyleActions(true)
.allowTextWithProgress(true)
.hideLargeIcon(true)
+ .hideAppName(isCollapsed)
+ .titleViewId(R.id.conversation_text)
+ .title(title)
.text(text)
.summaryText(mBuilder.processLegacyText(mVerificationText));
- RemoteViews contentView = mBuilder.applyStandardTemplate(
- mBuilder.getCallLayoutResource(), p, null /* result */);
-
- // Bind actions.
- mBuilder.resetStandardTemplateWithActions(contentView);
- mBuilder.bindSnoozeAction(contentView, p);
- bindCallActions(contentView, p);
+ mBuilder.mActions = getActionsListWithSystemActions();
+ final RemoteViews contentView;
+ if (isCollapsed) {
+ contentView = mBuilder.applyStandardTemplate(
+ R.layout.notification_template_material_call, p, null /* result */);
+ } else {
+ contentView = mBuilder.applyStandardTemplateWithActions(
+ R.layout.notification_template_material_big_call, p, null /* result */);
+ }
// Bind some extra conversation-specific header fields.
- mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
- mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
- contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+ if (!p.mHideAppName) {
+ mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+ contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+ }
bindCallerVerification(contentView, p);
// Bind some custom CallLayout properties
@@ -9427,41 +9511,6 @@ public class Notification implements Parcelable
return contentView;
}
- private void bindCallActions(RemoteViews view, StandardTemplateParams p) {
- view.setViewVisibility(R.id.actions_container, View.VISIBLE);
- view.setViewVisibility(R.id.actions, View.VISIBLE);
- view.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
- RemoteViews.MARGIN_BOTTOM, 0);
-
- // Clear view padding to allow buttons to start on the left edge.
- // This must be done before 'setEmphasizedMode' which sets top/bottom margins.
- view.setViewPadding(R.id.actions, 0, 0, 0, 0);
- // Add an optional indent that will make buttons start at the correct column when
- // there is enough space to do so (and fall back to the left edge if not).
- view.setInt(R.id.actions, "setCollapsibleIndentDimen",
- R.dimen.call_notification_collapsible_indent);
-
- // Emphasize so that buttons have borders or colored backgrounds
- boolean emphasizedMode = true;
- view.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
- // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
- // required actions (Answer, Decline, and Hang Up).
- view.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
-
- // Create the buttons for the generated actions list.
- int i = 0;
- for (Action action : makeActionsList(p)) {
- final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p);
- if (i > 0) {
- // Clear start margin from non-first buttons to reduce the gap between buttons.
- // (8dp remaining gap is from all buttons' standard 4dp inset).
- button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
- }
- view.addView(R.id.actions, button);
- ++i;
- }
- }
-
private void bindCallerVerification(RemoteViews contentView, StandardTemplateParams p) {
String iconContentDescription = null;
boolean showDivider = true;
@@ -12083,6 +12132,21 @@ public class Notification implements Parcelable
if (viewId == R.id.notification_header) {
views.setFloat(R.id.notification_header,
"setTopLineExtraMarginEndDp", marginEndDp);
+ } else if (viewId == R.id.text || viewId == R.id.big_text) {
+ if (mValueIfGone != 0) {
+ throw new RuntimeException("Programming error: `text` and `big_text` use "
+ + "ImageFloatingTextView which can either show a margin or not; "
+ + "thus mValueIfGone must be 0, but it was " + mValueIfGone);
+ }
+ // Note that the caller must set "setNumIndentLines" to a positive int in order
+ // for this margin to do anything at all.
+ views.setFloat(viewId, "setImageEndMarginDp", mValueIfVisible);
+ views.setBoolean(viewId, "setHasImage", mRightIconVisible);
+ // Apply just the *extra* margin as the view layout margin; this will be
+ // unchanged depending on the visibility of the image, but it means that the
+ // extra margin applies to *every* line of text instead of just indented lines.
+ views.setViewLayoutMargin(viewId, RemoteViews.MARGIN_END,
+ extraMarginDp, TypedValue.COMPLEX_UNIT_DIP);
} else {
views.setViewLayoutMargin(viewId, RemoteViews.MARGIN_END,
marginEndDp, TypedValue.COMPLEX_UNIT_DIP);
@@ -12108,19 +12172,22 @@ public class Notification implements Parcelable
public static int VIEW_TYPE_NORMAL = 1;
public static int VIEW_TYPE_BIG = 2;
public static int VIEW_TYPE_HEADS_UP = 3;
- public static int VIEW_TYPE_MINIMIZED = 4;
- public static int VIEW_TYPE_PUBLIC = 5;
- public static int VIEW_TYPE_GROUP_HEADER = 6;
+ public static int VIEW_TYPE_MINIMIZED = 4; // header only for minimized state
+ public static int VIEW_TYPE_PUBLIC = 5; // header only for automatic public version
+ public static int VIEW_TYPE_GROUP_HEADER = 6; // header only for top of group
int mViewType = VIEW_TYPE_UNSPECIFIED;
boolean mHeaderless;
+ boolean mHideAppName;
boolean mHideTitle;
boolean mHideActions;
boolean mHideProgress;
boolean mHideSnoozeButton;
boolean mPromotePicture;
- boolean mAllowActionIcons;
+ boolean mCallStyleActions;
boolean mAllowTextWithProgress;
+ int mTitleViewId;
+ int mTextViewId;
CharSequence title;
CharSequence text;
CharSequence headerTextSecondary;
@@ -12128,25 +12195,28 @@ public class Notification implements Parcelable
int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
boolean hideLargeIcon;
boolean allowColorization = true;
- boolean forceDefaultColor = false;
+ boolean mReduceHighlights = false;
final StandardTemplateParams reset() {
mViewType = VIEW_TYPE_UNSPECIFIED;
mHeaderless = false;
+ mHideAppName = false;
mHideTitle = false;
mHideActions = false;
mHideProgress = false;
mHideSnoozeButton = false;
mPromotePicture = false;
- mAllowActionIcons = false;
+ mCallStyleActions = false;
mAllowTextWithProgress = false;
+ mTitleViewId = R.id.title;
+ mTextViewId = R.id.text;
title = null;
text = null;
summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
allowColorization = true;
- forceDefaultColor = false;
+ mReduceHighlights = false;
return this;
}
@@ -12164,6 +12234,11 @@ public class Notification implements Parcelable
return this;
}
+ public StandardTemplateParams hideAppName(boolean hideAppName) {
+ mHideAppName = hideAppName;
+ return this;
+ }
+
final StandardTemplateParams hideActions(boolean hideActions) {
this.mHideActions = hideActions;
return this;
@@ -12179,8 +12254,8 @@ public class Notification implements Parcelable
return this;
}
- final StandardTemplateParams allowActionIcons(boolean allowActionIcons) {
- this.mAllowActionIcons = allowActionIcons;
+ final StandardTemplateParams callStyleActions(boolean callStyleActions) {
+ this.mCallStyleActions = callStyleActions;
return this;
}
@@ -12199,6 +12274,16 @@ public class Notification implements Parcelable
return this;
}
+ public StandardTemplateParams titleViewId(int titleViewId) {
+ mTitleViewId = titleViewId;
+ return this;
+ }
+
+ public StandardTemplateParams textViewId(int textViewId) {
+ mTextViewId = textViewId;
+ return this;
+ }
+
final StandardTemplateParams title(CharSequence title) {
this.title = title;
return this;
@@ -12229,8 +12314,8 @@ public class Notification implements Parcelable
return this;
}
- final StandardTemplateParams forceDefaultColor() {
- this.forceDefaultColor = true;
+ final StandardTemplateParams reduceHighlights() {
+ this.mReduceHighlights = true;
return this;
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index afcf63b0f1e2..1765849c8314 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -331,10 +331,12 @@ public class StatusBarManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Send {@link "
- + "android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} instead.")
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "This operation"
+ + " is not allowed anymore, please see {@link android.content"
+ + ".Intent#ACTION_CLOSE_SYSTEM_DIALOGS} for more details.")
@TestApi
public void collapsePanels() {
+
try {
final IStatusBarService svc = getService();
if (svc != null) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 43c14a99b221..31b0d413c395 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -129,11 +129,13 @@ import android.net.EthernetManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.IPacProxyManager;
import android.net.IVpnManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
+import android.net.PacProxyManager;
import android.net.TetheringManager;
import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
@@ -374,6 +376,15 @@ public final class SystemServiceRegistry {
// (which extends it).
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
+ registerService(Context.PAC_PROXY_SERVICE, PacProxyManager.class,
+ new CachedServiceFetcher<PacProxyManager>() {
+ @Override
+ public PacProxyManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.PAC_PROXY_SERVICE);
+ IPacProxyManager service = IPacProxyManager.Stub.asInterface(b);
+ return new PacProxyManager(ctx.getOuterContext(), service);
+ }});
+
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
public IBinder createService() throws ServiceNotFoundException {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8e53b5ba1c9f..3ef6757ade60 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -57,6 +57,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadSystemException;
+import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -67,6 +68,7 @@ import android.os.StrictMode;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
@@ -120,6 +122,8 @@ public class WallpaperManager {
/** {@hide} */
private static final String VALUE_CMF_COLOR =
android.os.SystemProperties.get("ro.boot.hardware.color");
+ /** {@hide} */
+ private static final String WALLPAPER_CMF_PATH = "/wallpaper/image/";
/**
* Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
@@ -315,8 +319,20 @@ public class WallpaperManager {
private int mCachedWallpaperUserId;
private Bitmap mDefaultWallpaper;
private Handler mMainLooperHandler;
- private ArrayMap<LocalWallpaperColorConsumer, ILocalWallpaperColorConsumer>
- mLocalColorCallbacks = new ArrayMap<>();
+ private ArrayMap<RectF, ArraySet<LocalWallpaperColorConsumer>> mLocalColorAreas =
+ new ArrayMap<>();
+ private ILocalWallpaperColorConsumer mLocalColorCallback =
+ new ILocalWallpaperColorConsumer.Stub() {
+ @Override
+ public void onColorsChanged(RectF area, WallpaperColors colors) {
+ ArraySet<LocalWallpaperColorConsumer> callbacks =
+ mLocalColorAreas.get(area);
+ if (callbacks == null) return;
+ for (LocalWallpaperColorConsumer callback: callbacks) {
+ callback.onColorsChanged(area, colors);
+ }
+ }
+ };
Globals(IWallpaperManager service, Looper looper) {
mService = service;
@@ -358,37 +374,46 @@ public class WallpaperManager {
}
}
- private ILocalWallpaperColorConsumer wrap(LocalWallpaperColorConsumer callback) {
- ILocalWallpaperColorConsumer callback2 = new ILocalWallpaperColorConsumer.Stub() {
- @Override
- public void onColorsChanged(RectF area, WallpaperColors colors) {
- callback.onColorsChanged(area, colors);
- }
- };
- mLocalColorCallbacks.put(callback, callback2);
- return callback2;
- }
-
public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
@NonNull List<RectF> regions, int which, int userId, int displayId) {
+ for (RectF area: regions) {
+ ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area);
+ if (callbacks == null) {
+ callbacks = new ArraySet<>();
+ mLocalColorAreas.put(area, callbacks);
+ }
+ callbacks.add(callback);
+ }
try {
- mService.addOnLocalColorsChangedListener(wrap(callback) , regions, which,
+ mService.addOnLocalColorsChangedListener(mLocalColorCallback , regions, which,
userId, displayId);
} catch (RemoteException e) {
// Can't get colors, connection lost.
+ Log.e(TAG, "Can't register for local color updates", e);
}
}
public void removeOnColorsChangedListener(
@NonNull LocalWallpaperColorConsumer callback, int which, int userId,
int displayId) {
- ILocalWallpaperColorConsumer callback2 = mLocalColorCallbacks.remove(callback);
- if (callback2 == null) return;
+ final ArrayList<RectF> removeAreas = new ArrayList<>();
+ for (RectF area : mLocalColorAreas.keySet()) {
+ ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area);
+ if (callbacks == null) continue;
+ callbacks.remove(callback);
+ if (callbacks.size() == 0) {
+ mLocalColorAreas.remove(area);
+ removeAreas.add(area);
+ }
+ }
try {
- mService.removeOnLocalColorsChangedListener(
- callback2, which, userId, displayId);
+ if (removeAreas.size() > 0) {
+ mService.removeOnLocalColorsChangedListener(
+ mLocalColorCallback, removeAreas, which, userId, displayId);
+ }
} catch (RemoteException e) {
// Can't get colors, connection lost.
+ Log.e(TAG, "Can't unregister for local color updates", e);
}
}
@@ -2066,31 +2091,45 @@ public class WallpaperManager {
return null;
} else {
whichProp = PROP_WALLPAPER;
- final int defaultColorResId = context.getResources().getIdentifier(
- "default_wallpaper_" + VALUE_CMF_COLOR, "drawable", "android");
- defaultResId =
- defaultColorResId == 0 ? com.android.internal.R.drawable.default_wallpaper
- : defaultColorResId;
+ defaultResId = com.android.internal.R.drawable.default_wallpaper;
}
final String path = SystemProperties.get(whichProp);
+ final InputStream wallpaperInputStream = getWallpaperInputStream(path);
+ if (wallpaperInputStream != null) {
+ return wallpaperInputStream;
+ }
+ final String cmfPath = getCmfWallpaperPath();
+ final InputStream cmfWallpaperInputStream = getWallpaperInputStream(cmfPath);
+ if (cmfWallpaperInputStream != null) {
+ return cmfWallpaperInputStream;
+ }
+ try {
+ return context.getResources().openRawResource(defaultResId);
+ } catch (NotFoundException e) {
+ // no default defined for this device; this is not a failure
+ }
+ return null;
+ }
+
+ private static InputStream getWallpaperInputStream(String path) {
if (!TextUtils.isEmpty(path)) {
final File file = new File(path);
if (file.exists()) {
try {
return new FileInputStream(file);
} catch (IOException e) {
- // Ignored, fall back to platform default below
+ // Ignored, fall back to platform default
}
}
}
- try {
- return context.getResources().openRawResource(defaultResId);
- } catch (NotFoundException e) {
- // no default defined for this device; this is not a failure
- }
return null;
}
+ private static String getCmfWallpaperPath() {
+ return Environment.getProductDirectory() + WALLPAPER_CMF_PATH + "default_wallpaper_"
+ + VALUE_CMF_COLOR;
+ }
+
/**
* Return {@link ComponentName} of the default live wallpaper, or
* {@code null} if none is defined.
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index 29792ac47a36..2298e84d755e 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -85,8 +85,10 @@ public class WindowTokenClient extends IWindowToken.Stub {
context.destroy();
mContextRef.clear();
}
- // If a secondary display is detached, release all views attached to this token.
- WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(),
- "WindowContext");
+ ActivityThread.currentActivityThread().getHandler().post(() -> {
+ // If the tracked window token is detached, release all views attached to this token.
+ WindowManagerGlobal.getInstance().closeAll(WindowTokenClient.this,
+ "#onWindowTokenRemoved()", "WindowTokenClient");
+ });
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ccf41e5f3063..56aa9a2b580c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -987,7 +987,8 @@ public class DevicePolicyManager {
* The default for this extra is {@code false} - by default, the admin of a fully-managed
* device has the ability to grant sensors-related permissions.
*
- * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only.
+ * <p>Use only for device owner provisioning.
+ * @see #ACTION_GET_PROVISIONING_MODE
*/
public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT =
"android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
@@ -2986,7 +2987,7 @@ public class DevicePolicyManager {
/**
* Checks if it's safe to run operations that can be affected by the given {@code reason}.
*
- * <p><b>Note:/b> notice that the operation safety state might change between the time this
+ * <p><b>Note:</b> notice that the operation safety state might change between the time this
* method returns and the operation's method is called, so calls to the latter could still throw
* a {@link UnsafeStateException} even when this method returns {@code true}.
*
@@ -11838,7 +11839,7 @@ public class DevicePolicyManager {
/**
* @hide
- * Force update user setup completed status. This API has no effect on user build.
+ * Force update user setup completed status.
* @throws {@link SecurityException} if the caller has no
* {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
* not {@link UserHandle#SYSTEM_USER}
@@ -13725,4 +13726,22 @@ public class DevicePolicyManager {
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Lists apps that are exempt from policies (such as
+ * {@link #setPackagesSuspended(ComponentName, String[], boolean)}).
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_DEVICE_ADMINS)
+ public @NonNull Set<String> getPolicyExemptApps() {
+ if (mService == null) return Collections.emptySet();
+
+ try {
+ return new HashSet<>(mService.listPolicyExemptApps());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 25ca59963d4b..e98720c0d96c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -177,6 +177,7 @@ interface IDevicePolicyManager {
String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended);
boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName);
+ List<String> listPolicyExemptApps();
boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases);
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 94ab0dd00113..9f8fcc15b747 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -253,7 +253,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
return other == null;
}
return other != null && mInfo.flags == other.flags
- && mInfo.maxAspectRatio == other.maxAspectRatio
+ && mInfo.getMaxAspectRatio() == other.getMaxAspectRatio()
&& Objects.equals(mInfo.launchToken, other.launchToken)
&& Objects.equals(mInfo.getComponentName(), other.getComponentName());
}
diff --git a/core/java/android/app/time/Capabilities.java b/core/java/android/app/time/Capabilities.java
new file mode 100644
index 000000000000..33db7211f3b4
--- /dev/null
+++ b/core/java/android/app/time/Capabilities.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A capability is the ability for the user to configure something or perform an action. This
+ * information is exposed so that system apps like SettingsUI can be dynamic, rather than
+ * hard-coding knowledge of when configuration or actions are applicable / available to the user.
+ *
+ * <p>Capabilities have states that users cannot change directly. They may influence some
+ * capabilities indirectly by agreeing to certain device-wide behaviors such as location sharing, or
+ * by changing the configuration. See the {@code CAPABILITY_} constants for details.
+ *
+ * <p>Actions have associated methods, see the documentation for each action for details.
+ *
+ * <p>Note: Capabilities are independent of app permissions required to call the associated APIs.
+ *
+ * @hide
+ */
+@SystemApi
+public final class Capabilities {
+
+ /** @hide */
+ @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE,
+ CAPABILITY_POSSESSED })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CapabilityState {}
+
+ /**
+ * Indicates that a capability is not supported on this device, e.g. because of form factor or
+ * hardware. The associated UI should usually not be shown to the user.
+ */
+ public static final int CAPABILITY_NOT_SUPPORTED = 10;
+
+ /**
+ * Indicates that a capability is supported on this device, but not allowed for the user, e.g.
+ * if the capability relates to the ability to modify settings the user is not able to.
+ * This could be because of the user's type (e.g. maybe it applies to the primary user only) or
+ * device policy. Depending on the capability, this could mean the associated UI
+ * should be hidden, or displayed but disabled.
+ */
+ public static final int CAPABILITY_NOT_ALLOWED = 20;
+
+ /**
+ * Indicates that a capability is possessed but not currently applicable, e.g. if the
+ * capability relates to the ability to modify settings, the user has the ability to modify
+ * it, but it is currently rendered irrelevant by other settings or other device state (flags,
+ * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but
+ * ineffective) depending on requirements.
+ */
+ public static final int CAPABILITY_NOT_APPLICABLE = 30;
+
+ /** Indicates that a capability is possessed by the user. */
+ public static final int CAPABILITY_POSSESSED = 40;
+
+ private Capabilities() {}
+
+}
diff --git a/core/java/android/app/time/TimeCapabilities.aidl b/core/java/android/app/time/TimeCapabilities.aidl
new file mode 100644
index 000000000000..f44b791e6ce7
--- /dev/null
+++ b/core/java/android/app/time/TimeCapabilities.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+parcelable TimeCapabilities;
diff --git a/core/java/android/app/time/TimeCapabilities.java b/core/java/android/app/time/TimeCapabilities.java
new file mode 100644
index 000000000000..fff36c4a7096
--- /dev/null
+++ b/core/java/android/app/time/TimeCapabilities.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.NonNull;
+import android.app.time.Capabilities.CapabilityState;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.util.Objects;
+
+/**
+ * Time-relate capabilities for a user.
+ *
+ * <p>For configuration settings capabilities, the associated settings value can be found via
+ * {@link TimeManager#getTimeCapabilitiesAndConfig()} and may be changed using {@link
+ * TimeManager#updateTimeConfiguration(TimeConfiguration)} (if the user's capabilities
+ * allow).
+ *
+ * @hide
+ */
+public final class TimeCapabilities implements Parcelable {
+
+ public static final @NonNull Creator<TimeCapabilities> CREATOR =
+ new Creator<TimeCapabilities>() {
+ public TimeCapabilities createFromParcel(Parcel in) {
+ return TimeCapabilities.createFromParcel(in);
+ }
+
+ public TimeCapabilities[] newArray(int size) {
+ return new TimeCapabilities[size];
+ }
+ };
+
+
+ /**
+ * The user the capabilities are for. This is used for object equality and debugging but there
+ * is no accessor.
+ */
+ @NonNull
+ private final UserHandle mUserHandle;
+ private final @CapabilityState int mConfigureAutoTimeDetectionEnabledCapability;
+ private final @CapabilityState int mSuggestTimeManuallyCapability;
+
+ private TimeCapabilities(@NonNull Builder builder) {
+ this.mUserHandle = Objects.requireNonNull(builder.mUserHandle);
+ this.mConfigureAutoTimeDetectionEnabledCapability =
+ builder.mConfigureAutoDetectionEnabledCapability;
+ this.mSuggestTimeManuallyCapability =
+ builder.mSuggestTimeManuallyCapability;
+ }
+
+ @NonNull
+ private static TimeCapabilities createFromParcel(Parcel in) {
+ UserHandle userHandle = UserHandle.readFromParcel(in);
+ return new TimeCapabilities.Builder(userHandle)
+ .setConfigureAutoTimeDetectionEnabledCapability(in.readInt())
+ .setSuggestTimeManuallyCapability(in.readInt())
+ .build();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ UserHandle.writeToParcel(mUserHandle, dest);
+ dest.writeInt(mConfigureAutoTimeDetectionEnabledCapability);
+ dest.writeInt(mSuggestTimeManuallyCapability);
+ }
+
+ /**
+ * Returns the capability state associated with the user's ability to modify the automatic time
+ * detection setting.
+ */
+ @CapabilityState
+ public int getConfigureAutoTimeDetectionEnabledCapability() {
+ return mConfigureAutoTimeDetectionEnabledCapability;
+ }
+
+ /**
+ * Returns the capability state associated with the user's ability to manually set time on a
+ * device.
+ */
+ @CapabilityState
+ public int getSuggestTimeManuallyCapability() {
+ return mSuggestTimeManuallyCapability;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TimeCapabilities that = (TimeCapabilities) o;
+ return mConfigureAutoTimeDetectionEnabledCapability
+ == that.mConfigureAutoTimeDetectionEnabledCapability
+ && mSuggestTimeManuallyCapability == that.mSuggestTimeManuallyCapability
+ && mUserHandle.equals(that.mUserHandle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUserHandle, mConfigureAutoTimeDetectionEnabledCapability,
+ mSuggestTimeManuallyCapability);
+ }
+
+ @Override
+ public String toString() {
+ return "TimeCapabilities{"
+ + "mUserHandle=" + mUserHandle
+ + ", mConfigureAutoTimeDetectionEnabledCapability="
+ + mConfigureAutoTimeDetectionEnabledCapability
+ + ", mSuggestTimeManuallyCapability=" + mSuggestTimeManuallyCapability
+ + '}';
+ }
+
+ /**
+ * A builder of {@link TimeCapabilities} objects.
+ *
+ * @hide
+ */
+ public static class Builder {
+ @NonNull private final UserHandle mUserHandle;
+ private @CapabilityState int mConfigureAutoDetectionEnabledCapability;
+ private @CapabilityState int mSuggestTimeManuallyCapability;
+
+ public Builder(@NonNull TimeCapabilities timeCapabilities) {
+ Objects.requireNonNull(timeCapabilities);
+ this.mUserHandle = timeCapabilities.mUserHandle;
+ this.mConfigureAutoDetectionEnabledCapability =
+ timeCapabilities.mConfigureAutoTimeDetectionEnabledCapability;
+ this.mSuggestTimeManuallyCapability =
+ timeCapabilities.mSuggestTimeManuallyCapability;
+ }
+
+ public Builder(@NonNull UserHandle userHandle) {
+ this.mUserHandle = Objects.requireNonNull(userHandle);
+ }
+
+ /** Sets the state for automatic time detection config. */
+ public Builder setConfigureAutoTimeDetectionEnabledCapability(
+ @CapabilityState int setConfigureAutoTimeDetectionEnabledCapability) {
+ this.mConfigureAutoDetectionEnabledCapability =
+ setConfigureAutoTimeDetectionEnabledCapability;
+ return this;
+ }
+
+ /** Sets the state for manual time change. */
+ public Builder setSuggestTimeManuallyCapability(
+ @CapabilityState int suggestTimeManuallyCapability) {
+ this.mSuggestTimeManuallyCapability = suggestTimeManuallyCapability;
+ return this;
+ }
+
+ /** Returns the {@link TimeCapabilities}. */
+ public TimeCapabilities build() {
+ verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability,
+ "configureAutoDetectionEnabledCapability");
+ verifyCapabilitySet(mSuggestTimeManuallyCapability, "suggestTimeManuallyCapability");
+ return new TimeCapabilities(this);
+ }
+
+ private void verifyCapabilitySet(int value, String name) {
+ if (value == 0) {
+ throw new IllegalStateException(name + " was not set");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl b/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl
new file mode 100644
index 000000000000..183dcaf6e2e6
--- /dev/null
+++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+parcelable TimeCapabilitiesAndConfig; \ No newline at end of file
diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.java b/core/java/android/app/time/TimeCapabilitiesAndConfig.java
new file mode 100644
index 000000000000..4a1044760064
--- /dev/null
+++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A pair containing a user's {@link TimeCapabilities} and {@link TimeConfiguration}.
+ *
+ * @hide
+ */
+public final class TimeCapabilitiesAndConfig implements Parcelable {
+
+ public static final @NonNull Creator<TimeCapabilitiesAndConfig> CREATOR =
+ new Creator<TimeCapabilitiesAndConfig>() {
+ @Override
+ public TimeCapabilitiesAndConfig createFromParcel(Parcel source) {
+ return TimeCapabilitiesAndConfig.readFromParcel(source);
+ }
+
+ @Override
+ public TimeCapabilitiesAndConfig[] newArray(int size) {
+ return new TimeCapabilitiesAndConfig[size];
+ }
+ };
+
+ @NonNull
+ private final TimeCapabilities mTimeCapabilities;
+
+ @NonNull
+ private final TimeConfiguration mTimeConfiguration;
+
+ /**
+ * @hide
+ */
+ public TimeCapabilitiesAndConfig(@NonNull TimeCapabilities timeCapabilities,
+ @NonNull TimeConfiguration timeConfiguration) {
+ mTimeCapabilities = Objects.requireNonNull(timeCapabilities);
+ mTimeConfiguration = Objects.requireNonNull(timeConfiguration);
+ }
+
+ @NonNull
+ private static TimeCapabilitiesAndConfig readFromParcel(Parcel in) {
+ TimeCapabilities capabilities = in.readParcelable(null);
+ TimeConfiguration configuration = in.readParcelable(null);
+ return new TimeCapabilitiesAndConfig(capabilities, configuration);
+ }
+
+ /**
+ * Returns the user's time behaviour capabilities.
+ *
+ * @hide
+ */
+ @NonNull
+ public TimeCapabilities getTimeCapabilities() {
+ return mTimeCapabilities;
+ }
+
+ /**
+ * Returns the user's time behaviour configuration.
+ *
+ * @hide
+ */
+ @NonNull
+ public TimeConfiguration getTimeConfiguration() {
+ return mTimeConfiguration;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mTimeCapabilities, flags);
+ dest.writeParcelable(mTimeConfiguration, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TimeCapabilitiesAndConfig that = (TimeCapabilitiesAndConfig) o;
+ return mTimeCapabilities.equals(that.mTimeCapabilities)
+ && mTimeConfiguration.equals(that.mTimeConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTimeCapabilities, mTimeConfiguration);
+ }
+
+ @Override
+ public String toString() {
+ return "TimeCapabilitiesAndConfig{"
+ + "mTimeCapabilities=" + mTimeCapabilities
+ + ", mTimeConfiguration=" + mTimeConfiguration
+ + '}';
+ }
+}
diff --git a/core/java/android/app/time/TimeConfiguration.aidl b/core/java/android/app/time/TimeConfiguration.aidl
new file mode 100644
index 000000000000..eb5bfd6d2272
--- /dev/null
+++ b/core/java/android/app/time/TimeConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+parcelable TimeConfiguration;
diff --git a/core/java/android/app/time/TimeConfiguration.java b/core/java/android/app/time/TimeConfiguration.java
new file mode 100644
index 000000000000..70aede034d27
--- /dev/null
+++ b/core/java/android/app/time/TimeConfiguration.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * User visible settings that control the behavior of the time zone detector / manual time zone
+ * entry.
+ *
+ * @hide
+ */
+public final class TimeConfiguration implements Parcelable {
+
+ public static final @NonNull Creator<TimeConfiguration> CREATOR =
+ new Creator<TimeConfiguration>() {
+ @Override
+ public TimeConfiguration createFromParcel(Parcel source) {
+ return TimeConfiguration.readFromParcel(source);
+ }
+
+ @Override
+ public TimeConfiguration[] newArray(int size) {
+ return new TimeConfiguration[size];
+ }
+ };
+
+ @StringDef(SETTING_AUTO_DETECTION_ENABLED)
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Setting {}
+
+ @Setting
+ private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
+
+ @NonNull
+ private final Bundle mBundle;
+
+ private TimeConfiguration(Builder builder) {
+ this.mBundle = builder.mBundle;
+ }
+
+ /**
+ * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This
+ * controls whether a device will attempt to determine the time automatically using
+ * contextual information if the device supports auto detection.
+ */
+ public boolean isAutoDetectionEnabled() {
+ return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(mBundle);
+ }
+
+ private static TimeConfiguration readFromParcel(Parcel in) {
+ return new TimeConfiguration.Builder()
+ .merge(in.readBundle())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TimeConfiguration that = (TimeConfiguration) o;
+ return mBundle.kindofEquals(that.mBundle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mBundle);
+ }
+
+ @Override
+ public String toString() {
+ return "TimeConfiguration{"
+ + "mBundle=" + mBundle
+ + '}';
+ }
+
+ /**
+ * A builder for {@link TimeConfiguration} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
+ private final Bundle mBundle = new Bundle();
+
+ public Builder() {}
+
+ public Builder(@NonNull TimeConfiguration configuration) {
+ mBundle.putAll(configuration.mBundle);
+ }
+
+ /** Sets whether auto detection is enabled or not. */
+ @NonNull
+ public Builder setAutoDetectionEnabled(boolean enabled) {
+ mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled);
+ return this;
+ }
+
+ Builder merge(@NonNull Bundle bundle) {
+ mBundle.putAll(bundle);
+ return this;
+ }
+
+ /** Returns {@link TimeConfiguration} object. */
+ @NonNull
+ public TimeConfiguration build() {
+ return new TimeConfiguration(this);
+ }
+ }
+}
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index 430960fb11a8..c8fa5c8f28e2 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -75,7 +75,7 @@ public final class TimeManager {
@NonNull
public TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() {
if (DEBUG) {
- Log.d(TAG, "getTimeZoneCapabilities called");
+ Log.d(TAG, "getTimeZoneCapabilitiesAndConfig called");
}
try {
return mITimeZoneDetectorService.getCapabilitiesAndConfig();
@@ -85,6 +85,44 @@ public final class TimeManager {
}
/**
+ * Returns the calling user's time capabilities and configuration.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ @NonNull
+ public TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig() {
+ if (DEBUG) {
+ Log.d(TAG, "getTimeCapabilitiesAndConfig called");
+ }
+ try {
+ return mITimeDetectorService.getCapabilitiesAndConfig();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Modifies the time detection configuration.
+ *
+ * @return {@code true} if all the configuration settings specified have been set to the
+ * new values, {@code false} if none have
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ public boolean updateTimeConfiguration(@NonNull TimeConfiguration configuration) {
+ if (DEBUG) {
+ Log.d(TAG, "updateTimeConfiguration called: " + configuration);
+ }
+ try {
+ return mITimeDetectorService.updateConfiguration(configuration);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Modifies the time zone detection configuration.
*
* <p>Configuration settings vary in scope: some may be global (affect all users), others may be
@@ -97,11 +135,11 @@ public final class TimeManager {
* capabilities.
*
* <p>Attempts to modify configuration settings with capabilities that are {@link
- * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link
- * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false}
+ * Capabilities#CAPABILITY_NOT_SUPPORTED} or {@link
+ * Capabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false}
* will be returned. Modifying configuration settings with capabilities that are {@link
- * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link
- * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link
+ * Capabilities#CAPABILITY_NOT_APPLICABLE} or {@link
+ * Capabilities#CAPABILITY_POSSESSED} will succeed. See {@link
* TimeZoneCapabilities} for further details.
*
* <p>If the supplied configuration only has some values set, then only the specified settings
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index a27be96e6e69..433b4200eece 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -16,77 +16,33 @@
package android.app.time;
-import android.annotation.IntDef;
+import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.time.Capabilities.CapabilityState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneDetector;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * Time zone-related capabilities for a user. A capability is the ability for the user to configure
- * something or perform an action. This information is exposed so that system apps like SettingsUI
- * can be dynamic, rather than hard-coding knowledge of when configuration or actions are applicable
- * / available to the user.
- *
- * <p>Capabilities have states that users cannot change directly. They may influence some
- * capabilities indirectly by agreeing to certain device-wide behaviors such as location sharing, or
- * by changing the configuration. See the {@code CAPABILITY_} constants for details.
- *
- * <p>Actions have associated methods, see the documentation for each action for details.
+ * Time zone-related capabilities for a user.
*
* <p>For configuration settings capabilities, the associated settings value can be found via
* {@link TimeManager#getTimeZoneCapabilitiesAndConfig()} and may be changed using {@link
* TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} (if the user's capabilities
* allow).
*
- * <p>Note: Capabilities are independent of app permissions required to call the associated APIs.
- *
* @hide
*/
@SystemApi
public final class TimeZoneCapabilities implements Parcelable {
- /** @hide */
- @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE,
- CAPABILITY_POSSESSED })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CapabilityState {}
-
- /**
- * Indicates that a capability is not supported on this device, e.g. because of form factor or
- * hardware. The associated UI should usually not be shown to the user.
- */
- public static final int CAPABILITY_NOT_SUPPORTED = 10;
-
- /**
- * Indicates that a capability is supported on this device, but not allowed for the user, e.g.
- * if the capability relates to the ability to modify settings the user is not able to.
- * This could be because of the user's type (e.g. maybe it applies to the primary user only) or
- * device policy. Depending on the capability, this could mean the associated UI
- * should be hidden, or displayed but disabled.
- */
- public static final int CAPABILITY_NOT_ALLOWED = 20;
-
- /**
- * Indicates that a capability is possessed but not currently applicable, e.g. if the
- * capability relates to the ability to modify settings, the user has the ability to modify
- * it, but it is currently rendered irrelevant by other settings or other device state (flags,
- * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but
- * ineffective) depending on requirements.
- */
- public static final int CAPABILITY_NOT_APPLICABLE = 30;
-
- /** Indicates that a capability is possessed by the user. */
- public static final int CAPABILITY_POSSESSED = 40;
-
public static final @NonNull Creator<TimeZoneCapabilities> CREATOR =
new Creator<TimeZoneCapabilities>() {
public TimeZoneCapabilities createFromParcel(Parcel in) {
@@ -159,7 +115,8 @@ public final class TimeZoneCapabilities implements Parcelable {
* on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
*
* <p>The suggestion will be ignored in all cases unless the value is {@link
- * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
+ * Capabilities#CAPABILITY_POSSESSED}. See also
+ * {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
*
* @hide
*/
diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
index f9a0c74312fc..a9ea76f77958 100644
--- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
@@ -113,7 +113,7 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
@Override
public String toString() {
- return "TimeZoneDetectorCapabilitiesAndConfig{"
+ return "TimeZoneCapabilitiesAndConfig{"
+ "mCapabilities=" + mCapabilities
+ ", mConfiguration=" + mConfiguration
+ '}';
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index c4546be10601..9a6c33589123 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -17,6 +17,8 @@
package android.app.timedetector;
import android.app.time.ExternalTimeSuggestion;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
@@ -36,6 +38,9 @@ import android.app.timedetector.TelephonyTimeSuggestion;
* {@hide}
*/
interface ITimeDetectorService {
+ TimeCapabilitiesAndConfig getCapabilitiesAndConfig();
+ boolean updateConfiguration(in TimeConfiguration timeConfiguration);
+
void suggestExternalTime( in ExternalTimeSuggestion timeSuggestion);
void suggestGnssTime(in GnssTimeSuggestion timeSuggestion);
boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 098d8b6c6058..9f1132b605ef 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.WorkerThread;
import android.app.usage.NetworkStats.Bucket;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -201,6 +202,7 @@ public class NetworkStatsManager {
* default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -219,6 +221,7 @@ public class NetworkStatsManager {
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public Bucket querySummaryForDevice(int networkType, String subscriberId,
long startTime, long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
@@ -240,6 +243,7 @@ public class NetworkStatsManager {
* uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
* metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
* {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,6 +262,7 @@ public class NetworkStatsManager {
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
@@ -283,6 +288,7 @@ public class NetworkStatsManager {
* means buckets' start and end timestamps are going to be the same as the 'startTime' and
* 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
* be the same.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -301,6 +307,7 @@ public class NetworkStatsManager {
* @return Statistics object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
@@ -326,9 +333,11 @@ public class NetworkStatsManager {
/**
* Query network usage statistics details for a given uid.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
+ @WorkerThread
public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
long startTime, long endTime, int uid) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -344,9 +353,11 @@ public class NetworkStatsManager {
/**
* Query network usage statistics details for a given uid and tag.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
+ @WorkerThread
public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -365,6 +376,7 @@ public class NetworkStatsManager {
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -387,6 +399,7 @@ public class NetworkStatsManager {
* @return Statistics object or null if an error happened during statistics collection.
* @throws SecurityException if permissions are insufficient to read network statistics.
*/
+ @WorkerThread
public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
NetworkTemplate template;
@@ -425,6 +438,7 @@ public class NetworkStatsManager {
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -443,6 +457,7 @@ public class NetworkStatsManager {
* @return Statistics object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index dcecd9070a74..5d50c5d77887 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -20,6 +20,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
@@ -109,6 +110,13 @@ public final class UsageStats implements Parcelable {
public long mTotalTimeForegroundServiceUsed;
/**
+ * Last time this package's component is used, measured in milliseconds since the epoch.
+ * See {@link UsageEvents.Event#APP_COMPONENT_USED}
+ * @hide
+ */
+ public long mLastTimeComponentUsed;
+
+ /**
* {@hide}
*/
@UnsupportedAppUsage
@@ -166,6 +174,7 @@ public final class UsageStats implements Parcelable {
mEndTimeStamp = stats.mEndTimeStamp;
mLastTimeUsed = stats.mLastTimeUsed;
mLastTimeVisible = stats.mLastTimeVisible;
+ mLastTimeComponentUsed = stats.mLastTimeComponentUsed;
mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
mTotalTimeInForeground = stats.mTotalTimeInForeground;
mTotalTimeVisible = stats.mTotalTimeVisible;
@@ -265,6 +274,16 @@ public final class UsageStats implements Parcelable {
}
/**
+ * Get the last time this package's component was used, measured in milliseconds since the
+ * epoch.
+ * @hide
+ */
+ @SystemApi
+ public long getLastTimeComponentUsed() {
+ return mLastTimeComponentUsed;
+ }
+
+ /**
* Returns the number of times the app was launched as an activity from outside of the app.
* Excludes intra-app activity transitions.
* @hide
@@ -323,6 +342,7 @@ public final class UsageStats implements Parcelable {
mergeEventMap(mForegroundServices, right.mForegroundServices);
mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
mLastTimeVisible = Math.max(mLastTimeVisible, right.mLastTimeVisible);
+ mLastTimeComponentUsed = Math.max(mLastTimeComponentUsed, right.mLastTimeComponentUsed);
mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed,
right.mLastTimeForegroundServiceUsed);
}
@@ -598,6 +618,9 @@ public final class UsageStats implements Parcelable {
mLastTimeVisible = timeStamp;
}
break;
+ case APP_COMPONENT_USED:
+ mLastTimeComponentUsed = timeStamp;
+ break;
default:
break;
}
@@ -620,6 +643,7 @@ public final class UsageStats implements Parcelable {
dest.writeLong(mEndTimeStamp);
dest.writeLong(mLastTimeUsed);
dest.writeLong(mLastTimeVisible);
+ dest.writeLong(mLastTimeComponentUsed);
dest.writeLong(mLastTimeForegroundServiceUsed);
dest.writeLong(mTotalTimeInForeground);
dest.writeLong(mTotalTimeVisible);
@@ -674,6 +698,7 @@ public final class UsageStats implements Parcelable {
stats.mEndTimeStamp = in.readLong();
stats.mLastTimeUsed = in.readLong();
stats.mLastTimeVisible = in.readLong();
+ stats.mLastTimeComponentUsed = in.readLong();
stats.mLastTimeForegroundServiceUsed = in.readLong();
stats.mTotalTimeInForeground = in.readLong();
stats.mTotalTimeVisible = in.readLong();
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index d79fac58cf12..a6b4b47f0db2 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -520,9 +520,7 @@ public class AppWidgetHostView extends FrameLayout {
return;
}
int layoutId = rvToApply.getLayoutId();
- // If our stale view has been prepared to match active, and the new
- // layout matches, try recycling it
- if (content == null && layoutId == mLayoutId) {
+ if (rvToApply.canRecycleView(mView)) {
try {
rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
mColorResources);
@@ -849,6 +847,7 @@ public class AppWidgetHostView extends FrameLayout {
public void setColorResources(@NonNull SparseIntArray colorMapping) {
mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping);
mLayoutId = -1;
+ mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
@@ -863,6 +862,7 @@ public class AppWidgetHostView extends FrameLayout {
if (mColorResources != null) {
mColorResources = null;
mLayoutId = -1;
+ mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 6ac1c1ae61ec..1cbb2fb3a8a3 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -121,7 +121,7 @@ public class AppWidgetProviderInfo implements Parcelable {
*
* @see #widgetFeatures
*/
- public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3;
+ public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4;
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ec94faa544d3..07dbdce3984f 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1311,7 +1311,6 @@ public final class BluetoothDevice implements Parcelable {
* the bonding process completes, and its result.
* <p>Android system services will handle the necessary user interactions
* to confirm and complete the bonding process.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @param transport The transport to use for the pairing procedure.
* @return false on immediate error, true if bonding will begin
@@ -1319,8 +1318,9 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean createBond(int transport) {
- return createBondOutOfBand(transport, null);
+ return createBondInternal(transport, null, null);
}
/**
@@ -1334,22 +1334,39 @@ public final class BluetoothDevice implements Parcelable {
* <p>Android system services will handle the necessary user interactions
* to confirm and complete the bonding process.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ * <p>There are two possible versions of OOB Data. This data can come in as
+ * P192 or P256. This is a reference to the cryptography used to generate the key.
+ * The caller may pass one or both. If both types of data are passed, then the
+ * P256 data will be preferred, and thus used.
*
* @param transport - Transport to use
- * @param oobData - Out Of Band data
+ * @param remoteP192Data - Out Of Band data (P192) or null
+ * @param remoteP256Data - Out Of Band data (P256) or null
* @return false on immediate error, true if bonding will begin
* @hide
*/
- public boolean createBondOutOfBand(int transport, OobData oobData) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
+ @Nullable OobData remoteP256Data) {
+ if (remoteP192Data == null && remoteP256Data == null) {
+ throw new IllegalArgumentException(
+ "One or both arguments for the OOB data types are required to not be null."
+ + " Please use createBond() instead if you do not have OOB data to pass.");
+ }
+ return createBondInternal(transport, remoteP192Data, remoteP256Data);
+ }
+
+ private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
+ @Nullable OobData remoteP256Data) {
final IBluetooth service = sService;
if (service == null) {
Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
return false;
}
try {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- return service.createBond(this, transport, oobData, adapter.getOpPackageName());
+ return service.createBond(this, transport, remoteP192Data, remoteP256Data,
+ BluetoothAdapter.getDefaultAdapter().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1380,27 +1397,6 @@ public final class BluetoothDevice implements Parcelable {
}
/**
- * Set the Out Of Band data for a remote device to be used later
- * in the pairing mechanism. Users can obtain this data through other
- * trusted channels
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
- *
- * @param hash Simple Secure pairing hash
- * @param randomizer The random key obtained using OOB
- * @return false on error; true otherwise
- * @hide
- */
- public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
- //TODO(BT)
- /*
- try {
- return sService.setDeviceOutOfBandData(this, hash, randomizer);
- } catch (RemoteException e) {Log.e(TAG, "", e);} */
- return false;
- }
-
- /**
* Cancel an in-progress bonding request started with {@link #createBond}.
*
* @return true on success, false on error
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 0d0c6ab2efa9..08d694eb93e2 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,88 +16,949 @@
package android.bluetooth;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.lang.IllegalArgumentException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Out Of Band Data for Bluetooth device pairing.
*
* <p>This object represents optional data obtained from a remote device through
- * an out-of-band channel (eg. NFC).
+ * an out-of-band channel (eg. NFC, QR).
+ *
+ * <p>References:
+ * NFC AD Forum SSP 1.1 (AD)
+ * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf}
+ * Core Specification Supplement (CSS) V9
+ *
+ * <p>There are several BR/EDR Examples
+ *
+ * <p>Negotiated Handover:
+ * Bluetooth Carrier Configuration Record:
+ * - OOB Data Length
+ * - Device Address
+ * - Class of Device
+ * - Simple Pairing Hash C
+ * - Simple Pairing Randomizer R
+ * - Service Class UUID
+ * - Bluetooth Local Name
+ *
+ * <p>Static Handover:
+ * Bluetooth Carrier Configuration Record:
+ * - OOB Data Length
+ * - Device Address
+ * - Class of Device
+ * - Service Class UUID
+ * - Bluetooth Local Name
+ *
+ * <p>Simplified Tag Format for Single BT Carrier:
+ * Bluetooth OOB Data Record:
+ * - OOB Data Length
+ * - Device Address
+ * - Class of Device
+ * - Service Class UUID
+ * - Bluetooth Local Name
*
* @hide
*/
-public class OobData implements Parcelable {
- private byte[] mLeBluetoothDeviceAddress;
- private byte[] mSecurityManagerTk;
- private byte[] mLeSecureConnectionsConfirmation;
- private byte[] mLeSecureConnectionsRandom;
+@SystemApi
+public final class OobData implements Parcelable {
+
+ private static final String TAG = "OobData";
+ /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int OOB_LENGTH_OCTETS = 2;
+ /**
+ * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1).
+ * (AD 3.1.2) (CSS 1.6.2)
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_ADDRESS_OCTETS = 7;
+ /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int CLASS_OF_DEVICE_OCTETS = 3;
+ /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int CONFIRMATION_OCTETS = 16;
+ /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
+ @SystemApi
+ public static final int RANDOMIZER_OCTETS = 16;
+ /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_OCTETS = 1;
+ /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
+ @SystemApi
+ public static final int LE_TK_OCTETS = 16;
+ /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
+ @SystemApi
+ public static final int LE_APPEARANCE_OCTETS = 2;
+ /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
+ @SystemApi
+ public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
+
+ // Le Roles
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "LE_DEVICE_ROLE_" },
+ value = {
+ LE_DEVICE_ROLE_PERIPHERAL_ONLY,
+ LE_DEVICE_ROLE_CENTRAL_ONLY,
+ LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
+ LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
+ }
+ )
+ public @interface LeRole {}
+
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
+ /** @hide */
+ @SystemApi
+ public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
+
+ // Le Flags
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "LE_FLAG_" },
+ value = {
+ LE_FLAG_LIMITED_DISCOVERY_MODE,
+ LE_FLAG_GENERAL_DISCOVERY_MODE,
+ LE_FLAG_BREDR_NOT_SUPPORTED,
+ LE_FLAG_SIMULTANEOUS_CONTROLLER,
+ LE_FLAG_SIMULTANEOUS_HOST
+ }
+ )
+ public @interface LeFlag {}
+
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
+ /** @hide */
+ @SystemApi
+ public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
+
+ /**
+ * Main creation method for creating a Classic version of {@link OobData}.
+ *
+ * <p>This object will allow the caller to call {@link ClassicBuilder#build()}
+ * to build the data object or add any option information to the builder.
+ *
+ * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required for pairing OOB.
+ * @param classicLength byte array representing the length of data from 8-65535 across 2
+ * octets (0xXXXX).
+ * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+ * that owns the OOB data. (i.e. the originator) [6 octets]
+ *
+ * @return a Classic Builder instance with all the given data set or null.
+ *
+ * @throws IllegalArgumentException if any of the values fail to be set.
+ * @throws NullPointerException if any argument is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash,
+ @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) {
+ return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType);
+ }
+
+ /**
+ * Main creation method for creating a LE version of {@link OobData}.
+ *
+ * <p>This object will allow the caller to call {@link LeBuilder#build()}
+ * to build the data object or add any option information to the builder.
+ *
+ * @param deviceAddressWithType the LE device address plus the address type (7 octets);
+ * not null.
+ * @param leDeviceRole whether the device supports Peripheral, Central,
+ * Both including preference; not null. (1 octet)
+ * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is
+ * required for pairing OOB.
+ *
+ * <p>Possible LE Device Role Values:
+ * 0x00 Only Peripheral supported
+ * 0x01 Only Central supported
+ * 0x02 Central & Peripheral supported; Peripheral Preferred
+ * 0x03 Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ *
+ * @return a LeBuilder instance with all the given data set or null.
+ *
+ * @throws IllegalArgumentException if any of the values fail to be set.
+ * @throws NullPointerException if any argument is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash,
+ @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) {
+ return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole);
+ }
+
+ /**
+ * Builds an {@link OobData} object and validates that the required combination
+ * of values are present to create the LE specific OobData type.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class LeBuilder {
+
+ /**
+ * It is recommended that this Hash C is generated anew for each
+ * pairing.
+ *
+ * <p>It should be noted that on passive NFC this isn't possible as the data is static
+ * and immutable.
+ */
+ private byte[] mConfirmationHash = null;
+
+ /**
+ * Optional, but adds more validity to the pairing.
+ *
+ * <p>If not present a value of 0 is assumed.
+ */
+ private byte[] mRandomizerHash = new byte[] {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ };
+
+ /**
+ * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+ *
+ * <p>This is the name that may be displayed to the device user as part of the UI.
+ */
+ private byte[] mDeviceName = null;
+
+ /**
+ * Sets the Bluetooth Device name to be used for UI purposes.
+ *
+ * <p>Optional attribute.
+ *
+ * @param deviceName byte array representing the name, may be 0 in length, not null.
+ *
+ * @return {@link OobData#ClassicBuilder}
+ *
+ * @throws NullPointerException if deviceName is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
+ Preconditions.checkNotNull(deviceName);
+ this.mDeviceName = deviceName;
+ return this;
+ }
+
+ /**
+ * The Bluetooth Device Address is the address to which the OOB data belongs.
+ *
+ * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+ *
+ * <p> Address is encoded in Little Endian order.
+ *
+ * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+ */
+ private final byte[] mDeviceAddressWithType;
+
+ /**
+ * During an LE connection establishment, one must be in the Peripheral mode and the other
+ * in the Central role.
+ *
+ * <p>Possible Values:
+ * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+ * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+ * Peripheral Preferred
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ */
+ private final @LeRole int mLeDeviceRole;
+
+ /**
+ * Temporary key value from the Security Manager.
+ *
+ * <p> Must be {@link LE_TK_OCTETS} in size
+ */
+ private byte[] mLeTemporaryKey = null;
+
+ /**
+ * Defines the representation of the external appearance of the device.
+ *
+ * <p>For example, a mouse, remote control, or keyboard.
+ *
+ * <p>Used for visual on discovering device to represent icon/string/etc...
+ */
+ private byte[] mLeAppearance = null;
+
+ /**
+ * Contains which discoverable mode to use, BR/EDR support and capability.
+ *
+ * <p>Possible LE Flags:
+ * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+ * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+ * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+ * LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Controller).
+ * Bit 49 of LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Host).
+ * Bit 55 of LMP Feature Mask Definitions.
+ * <b>0x05- 0x07 Reserved</b>
+ */
+ private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
+
+ /**
+ * Constructing an OobData object for use with LE requires
+ * a LE Device Address and LE Device Role as well as the Confirmation
+ * and optionally, the Randomizer, however it is recommended to use.
+ *
+ * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+ * octets of data. Data is derived from controller/host stack and is required for
+ * pairing OOB.
+ * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte
+ * address type.
+ * @param leDeviceRole indicating device's role and preferences (Central or Peripheral)
+ *
+ * <p>Possible Values:
+ * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+ * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+ * Peripheral Preferred
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ *
+ * @throws IllegalArgumentException if deviceAddressWithType is not
+ * {@link LE_DEVICE_ADDRESS_OCTETS} octets
+ * @throws NullPointerException if any argument is null.
+ */
+ private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType,
+ @LeRole int leDeviceRole) {
+ Preconditions.checkNotNull(confirmationHash);
+ Preconditions.checkNotNull(deviceAddressWithType);
+ if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+ throw new IllegalArgumentException("confirmationHash must be "
+ + OobData.CONFIRMATION_OCTETS + " octets in length.");
+ }
+ this.mConfirmationHash = confirmationHash;
+ if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
+ throw new IllegalArgumentException("confirmationHash must be "
+ + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length.");
+ }
+ this.mDeviceAddressWithType = deviceAddressWithType;
+ if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
+ || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
+ throw new IllegalArgumentException("leDeviceRole must be a valid value.");
+ }
+ this.mLeDeviceRole = leDeviceRole;
+ }
+
+ /**
+ * Sets the Temporary Key value to be used by the LE Security Manager during
+ * LE pairing.
+ *
+ * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6,
+ * Part A 1.8 for a detailed description.
+ *
+ * @return {@link OobData#Builder}
+ *
+ * @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
+ * @throws NullinterException if leTemporaryKey is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
+ Preconditions.checkNotNull(leTemporaryKey);
+ if (leTemporaryKey.length != LE_TK_OCTETS) {
+ throw new IllegalArgumentException("leTemporaryKey must be "
+ + LE_TK_OCTETS + " octets in length.");
+ }
+ this.mLeTemporaryKey = leTemporaryKey;
+ return this;
+ }
+
+ /**
+ * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required for pairing OOB.
+ * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+ *
+ * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+ * @throws NullPointerException if randomizerHash is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+ Preconditions.checkNotNull(randomizerHash);
+ if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+ throw new IllegalArgumentException("randomizerHash must be "
+ + OobData.RANDOMIZER_OCTETS + " octets in length.");
+ }
+ this.mRandomizerHash = randomizerHash;
+ return this;
+ }
+
+ /**
+ * Sets the LE Flags necessary for the pairing scenario or discovery mode.
+ *
+ * @param leFlags enum value representing the 1 octet of data about discovery modes.
+ *
+ * <p>Possible LE Flags:
+ * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+ * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+ * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+ * LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Host).
+ * Bit 55 of LMP Feature Mask Definitions.
+ * 0x05- 0x07 Reserved
+ *
+ * @throws IllegalArgumentException for invalid flag
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public LeBuilder setLeFlags(@LeFlag int leFlags) {
+ if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
+ throw new IllegalArgumentException("leFlags must be a valid value.");
+ }
+ this.mLeFlags = leFlags;
+ return this;
+ }
+
+ /**
+ * Validates and builds the {@link OobData} object for LE Security.
+ *
+ * @return {@link OobData} with given builder values
+ *
+ * @throws IllegalStateException if either of the 2 required fields were not set.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public OobData build() {
+ final OobData oob =
+ new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole,
+ this.mConfirmationHash);
+
+ // If we have values, set them, otherwise use default
+ oob.mLeTemporaryKey =
+ (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
+ oob.mLeAppearance = (this.mLeAppearance != null)
+ ? this.mLeAppearance : oob.mLeAppearance;
+ oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
+ oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+ oob.mRandomizerHash = this.mRandomizerHash;
+ return oob;
+ }
+ }
+
+ /**
+ * Builds an {@link OobData} object and validates that the required combination
+ * of values are present to create the Classic specific OobData type.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class ClassicBuilder {
+ // Used by both Classic and LE
+ /**
+ * It is recommended that this Hash C is generated anew for each
+ * pairing.
+ *
+ * <p>It should be noted that on passive NFC this isn't possible as the data is static
+ * and immutable.
+ *
+ * @hide
+ */
+ private byte[] mConfirmationHash = null;
+
+ /**
+ * Optional, but adds more validity to the pairing.
+ *
+ * <p>If not present a value of 0 is assumed.
+ *
+ * @hide
+ */
+ private byte[] mRandomizerHash = new byte[] {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ };
+
+ /**
+ * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+ *
+ * <p>This is the name that may be displayed to the device user as part of the UI.
+ *
+ * @hide
+ */
+ private byte[] mDeviceName = null;
+
+ /**
+ * This length value provides the absolute length of total OOB data block used for
+ * Bluetooth BR/EDR
+ *
+ * <p>OOB communication, which includes the length field itself and the Bluetooth
+ * Device Address.
+ *
+ * <p>The minimum length that may be represented in this field is 8.
+ *
+ * @hide
+ */
+ private final byte[] mClassicLength;
+
+ /**
+ * The Bluetooth Device Address is the address to which the OOB data belongs.
+ *
+ * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+ *
+ * <p> Address is encoded in Little Endian order.
+ *
+ * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+ *
+ * @hide
+ */
+ private final byte[] mDeviceAddressWithType;
+
+ /**
+ * Class of Device information is to be used to provide a graphical representation
+ * to the user as part of UI involving operations.
+ *
+ * <p>This is not to be used to determine a particular service can be used.
+ *
+ * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+ *
+ * @hide
+ */
+ private byte[] mClassOfDevice = null;
+
+ /**
+ * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+ * octets of data. Data is derived from controller/host stack and is required for pairing
+ * OOB.
+ * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required
+ * for pairing OOB. Also, randomizerHash may be all 0s or null in which case
+ * it becomes all 0s.
+ * @param classicLength byte array representing the length of data from 8-65535 across 2
+ * octets (0xXXXX). Inclusive of this value in the length.
+ * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+ * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type
+ * as the last octet.
+ *
+ * @throws IllegalArgumentException if any value is not the correct length
+ * @throws NullPointerException if anything passed is null
+ *
+ * @hide
+ */
+ private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength,
+ @NonNull byte[] deviceAddressWithType) {
+ Preconditions.checkNotNull(confirmationHash);
+ Preconditions.checkNotNull(classicLength);
+ Preconditions.checkNotNull(deviceAddressWithType);
+ if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+ throw new IllegalArgumentException("confirmationHash must be "
+ + OobData.CONFIRMATION_OCTETS + " octets in length.");
+ }
+ this.mConfirmationHash = confirmationHash;
+ if (classicLength.length != OOB_LENGTH_OCTETS) {
+ throw new IllegalArgumentException("classicLength must be "
+ + OOB_LENGTH_OCTETS + " octets in length.");
+ }
+ this.mClassicLength = classicLength;
+ if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
+ throw new IllegalArgumentException("deviceAddressWithType must be "
+ + DEVICE_ADDRESS_OCTETS + " octets in length.");
+ }
+ this.mDeviceAddressWithType = deviceAddressWithType;
+ }
- public byte[] getLeBluetoothDeviceAddress() {
- return mLeBluetoothDeviceAddress;
+ /**
+ * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+ * of data. Data is derived from controller/host stack and is required for pairing OOB.
+ * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+ *
+ * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+ * @throws NullPointerException if randomizerHash is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+ Preconditions.checkNotNull(randomizerHash);
+ if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+ throw new IllegalArgumentException("randomizerHash must be "
+ + OobData.RANDOMIZER_OCTETS + " octets in length.");
+ }
+ this.mRandomizerHash = randomizerHash;
+ return this;
+ }
+
+ /**
+ * Sets the Bluetooth Device name to be used for UI purposes.
+ *
+ * <p>Optional attribute.
+ *
+ * @param deviceName byte array representing the name, may be 0 in length, not null.
+ *
+ * @return {@link OobData#ClassicBuilder}
+ *
+ * @throws NullPointerException if deviceName is null
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
+ Preconditions.checkNotNull(deviceName);
+ this.mDeviceName = deviceName;
+ return this;
+ }
+
+ /**
+ * Sets the Bluetooth Class of Device; used for UI purposes only.
+ *
+ * <p>Not an indicator of available services!
+ *
+ * <p>Optional attribute.
+ *
+ * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+ *
+ * @return {@link OobData#ClassicBuilder}
+ *
+ * @throws IllegalArgumentException if length is not equal to
+ * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+ * @throws NullPointerException if classOfDevice is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
+ Preconditions.checkNotNull(classOfDevice);
+ if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
+ throw new IllegalArgumentException("classOfDevice must be "
+ + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length.");
+ }
+ this.mClassOfDevice = classOfDevice;
+ return this;
+ }
+
+ /**
+ * Validates and builds the {@link OobDat object for Classic Security.
+ *
+ * @return {@link OobData} with previously given builder values.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public OobData build() {
+ final OobData oob =
+ new OobData(this.mClassicLength, this.mDeviceAddressWithType,
+ this.mConfirmationHash);
+ // If we have values, set them, otherwise use default
+ oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+ oob.mClassOfDevice = (this.mClassOfDevice != null)
+ ? this.mClassOfDevice : oob.mClassOfDevice;
+ oob.mRandomizerHash = this.mRandomizerHash;
+ return oob;
+ }
+ }
+
+ // Members (Defaults for Optionals must be set or Parceling fails on NPE)
+ // Both
+ private final byte[] mDeviceAddressWithType;
+ private final byte[] mConfirmationHash;
+ private byte[] mRandomizerHash = new byte[] {
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ };
+ // Default the name to "Bluetooth Device"
+ private byte[] mDeviceName = new byte[] {
+ // Bluetooth
+ 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
+ // <space>Device
+ 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65
+ };
+
+ // Classic
+ private final byte[] mClassicLength;
+ private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
+
+ // LE
+ private final @LeRole int mLeDeviceRole;
+ private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
+ private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
+ private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
+
+ /**
+ * @return byte array representing the MAC address of a bluetooth device.
+ * The Address is 6 octets long with a 1 octet address type associated with the address.
+ *
+ * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type.
+ * For LE there are more choices for Address Type.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getDeviceAddressWithType() {
+ return mDeviceAddressWithType;
+ }
+
+ /**
+ * @return byte array representing the confirmationHash value
+ * which is used to confirm the identity to the controller.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getConfirmationHash() {
+ return mConfirmationHash;
}
/**
- * Sets the LE Bluetooth Device Address value to be used during LE pairing.
- * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for
- * a detailed description.
+ * @return byte array representing the randomizerHash value
+ * which is used to verify the identity of the controller.
+ *
+ * @hide
*/
- public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) {
- mLeBluetoothDeviceAddress = leBluetoothDeviceAddress;
+ @NonNull
+ @SystemApi
+ public byte[] getRandomizerHash() {
+ return mRandomizerHash;
}
- public byte[] getSecurityManagerTk() {
- return mSecurityManagerTk;
+ /**
+ * @return Device Name used for displaying name in UI.
+ *
+ * <p>Also, this will be populated with the LE Local Name if the data is for LE.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public byte[] getDeviceName() {
+ return mDeviceName;
+ }
+
+ /**
+ * @return byte array representing the oob data length which is the length
+ * of all of the data including these octets.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getClassicLength() {
+ return mClassicLength;
+ }
+
+ /**
+ * @return byte array representing the class of device for UI display.
+ *
+ * <p>Does not indicate services available; for display only.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public byte[] getClassOfDevice() {
+ return mClassOfDevice;
}
/**
- * Sets the Temporary Key value to be used by the LE Security Manager during
- * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6,
- * Part A 1.8 for a detailed description.
+ * @return Temporary Key used for LE pairing.
+ *
+ * @hide
*/
- public void setSecurityManagerTk(byte[] securityManagerTk) {
- mSecurityManagerTk = securityManagerTk;
+ @Nullable
+ @SystemApi
+ public byte[] getLeTemporaryKey() {
+ return mLeTemporaryKey;
}
- public byte[] getLeSecureConnectionsConfirmation() {
- return mLeSecureConnectionsConfirmation;
+ /**
+ * @return Appearance used for LE pairing. For use in UI situations
+ * when determining what sort of icons or text to display regarding
+ * the device.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public byte[] getLeAppearance() {
+ return mLeTemporaryKey;
}
- public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) {
- mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation;
+ /**
+ * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability.
+ *
+ * <p>Possible LE Flags:
+ * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+ * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+ * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+ * LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Controller).
+ * Bit 49 of LMP Feature Mask Definitions.
+ * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+ * Same Device Capable (Host).
+ * Bit 55 of LMP Feature Mask Definitions.
+ * <b>0x05- 0x07 Reserved</b>
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @LeFlag
+ public int getLeFlags() {
+ return mLeFlags;
}
- public byte[] getLeSecureConnectionsRandom() {
- return mLeSecureConnectionsRandom;
+ /**
+ * @return the supported and preferred roles of the LE device.
+ *
+ * <p>Possible Values:
+ * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+ * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+ * Peripheral Preferred
+ * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+ * 0x04 - 0xFF Reserved
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @LeRole
+ public int getLeDeviceRole() {
+ return mLeDeviceRole;
}
- public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) {
- mLeSecureConnectionsRandom = leSecureConnectionsRandom;
+ /**
+ * Classic Security Constructor
+ */
+ private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType,
+ @NonNull byte[] confirmationHash) {
+ mClassicLength = classicLength;
+ mDeviceAddressWithType = deviceAddressWithType;
+ mConfirmationHash = confirmationHash;
+ mLeDeviceRole = -1; // Satisfy final
}
- public OobData() {
+ /**
+ * LE Security Constructor
+ */
+ private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole,
+ @NonNull byte[] confirmationHash) {
+ mDeviceAddressWithType = deviceAddressWithType;
+ mLeDeviceRole = leDeviceRole;
+ mConfirmationHash = confirmationHash;
+ mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
}
private OobData(Parcel in) {
- mLeBluetoothDeviceAddress = in.createByteArray();
- mSecurityManagerTk = in.createByteArray();
- mLeSecureConnectionsConfirmation = in.createByteArray();
- mLeSecureConnectionsRandom = in.createByteArray();
+ // Both
+ mDeviceAddressWithType = in.createByteArray();
+ mConfirmationHash = in.createByteArray();
+ mRandomizerHash = in.createByteArray();
+ mDeviceName = in.createByteArray();
+
+ // Classic
+ mClassicLength = in.createByteArray();
+ mClassOfDevice = in.createByteArray();
+
+ // LE
+ mLeDeviceRole = in.readInt();
+ mLeTemporaryKey = in.createByteArray();
+ mLeAppearance = in.createByteArray();
+ mLeFlags = in.readInt();
}
+ /**
+ * @hide
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * @hide
+ */
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeByteArray(mLeBluetoothDeviceAddress);
- out.writeByteArray(mSecurityManagerTk);
- out.writeByteArray(mLeSecureConnectionsConfirmation);
- out.writeByteArray(mLeSecureConnectionsRandom);
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ // Both
+ // Required
+ out.writeByteArray(mDeviceAddressWithType);
+ // Required
+ out.writeByteArray(mConfirmationHash);
+ // Optional
+ out.writeByteArray(mRandomizerHash);
+ // Optional
+ out.writeByteArray(mDeviceName);
+
+ // Classic
+ // Required
+ out.writeByteArray(mClassicLength);
+ // Optional
+ out.writeByteArray(mClassOfDevice);
+
+ // LE
+ // Required
+ out.writeInt(mLeDeviceRole);
+ // Required
+ out.writeByteArray(mLeTemporaryKey);
+ // Optional
+ out.writeByteArray(mLeAppearance);
+ // Optional
+ out.writeInt(mLeFlags);
}
+ // For Parcelable
public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
new Parcelable.Creator<OobData>() {
public OobData createFromParcel(Parcel in) {
@@ -108,4 +969,47 @@ public class OobData implements Parcelable {
return new OobData[size];
}
};
+
+ /**
+ * @return a {@link String} representation of the OobData object.
+ *
+ * @hide
+ */
+ @Override
+ @NonNull
+ public String toString() {
+ return "OobData: \n\t"
+ // Both
+ + "Device Address With Type: " + toHexString(mDeviceAddressWithType) + "\n\t"
+ + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t"
+ + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t"
+ + "Device Name: " + toHexString(mDeviceName) + "\n\t"
+ // Classic
+ + "OobData Length: " + toHexString(mClassicLength) + "\n\t"
+ + "Class of Device: " + toHexString(mClassOfDevice) + "\n\t"
+ // LE
+ + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t"
+ + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t"
+ + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t"
+ + "LE Flags: " + toHexString(mLeFlags) + "\n\t";
+ }
+
+ @NonNull
+ private String toHexString(@NonNull int b) {
+ return toHexString(new byte[] {(byte) b});
+ }
+
+ @NonNull
+ private String toHexString(@NonNull byte b) {
+ return toHexString(new byte[] {b});
+ }
+
+ @NonNull
+ private String toHexString(@NonNull byte[] array) {
+ StringBuilder builder = new StringBuilder(array.length * 2);
+ for (byte b: array) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index f3ecbf6c08f3..6ab1975f9717 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -21,6 +21,7 @@ import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
import static android.content.ContentResolver.SCHEME_CONTENT;
import static android.content.ContentResolver.SCHEME_FILE;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.AssetFileDescriptor;
@@ -38,6 +39,7 @@ import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.textclassifier.TextLinks;
import com.android.internal.util.ArrayUtils;
@@ -204,6 +206,7 @@ public class ClipData implements Parcelable {
Uri mUri;
// Additional activity info resolved by the system
ActivityInfo mActivityInfo;
+ private TextLinks mTextLinks;
/** @hide */
public Item(Item other) {
@@ -332,6 +335,29 @@ public class ClipData implements Parcelable {
}
/**
+ * Returns the results of text classification run on the raw text contained in this item,
+ * if it was performed, and if any entities were found in the text. Classification is
+ * generally only performed on the first item in clip data, and only if the text is below a
+ * certain length.
+ *
+ * <p>Returns {@code null} if classification was not performed, or if no entities were
+ * found in the text.
+ *
+ * @see ClipDescription#getConfidenceScore(String)
+ */
+ @Nullable
+ public TextLinks getTextLinks() {
+ return mTextLinks;
+ }
+
+ /**
+ * @hide
+ */
+ public void setTextLinks(TextLinks textLinks) {
+ mTextLinks = textLinks;
+ }
+
+ /**
* Turn this item into text, regardless of the type of data it
* actually contains.
*
@@ -1183,6 +1209,7 @@ public class ClipData implements Parcelable {
dest.writeTypedObject(item.mIntent, flags);
dest.writeTypedObject(item.mUri, flags);
dest.writeTypedObject(item.mActivityInfo, flags);
+ dest.writeTypedObject(item.mTextLinks, flags);
}
}
@@ -1201,8 +1228,10 @@ public class ClipData implements Parcelable {
Intent intent = in.readTypedObject(Intent.CREATOR);
Uri uri = in.readTypedObject(Uri.CREATOR);
ActivityInfo info = in.readTypedObject(ActivityInfo.CREATOR);
+ TextLinks textLinks = in.readTypedObject(TextLinks.CREATOR);
Item item = new Item(text, htmlText, intent, uri);
item.setActivityInfo(info);
+ item.setTextLinks(textLinks);
mItems.add(item);
}
}
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index d48f83223d3e..f49362e9300e 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -16,16 +16,25 @@
package android.content;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Map;
/**
* Meta-data describing the contents of a {@link ClipData}. Provides enough
@@ -115,12 +124,39 @@ public class ClipDescription implements Parcelable {
*/
public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value =
+ { CLASSIFICATION_NOT_COMPLETE, CLASSIFICATION_NOT_PERFORMED, CLASSIFICATION_COMPLETE})
+ @interface ClassificationStatus {}
+
+ /**
+ * Value returned by {@link #getConfidenceScore(String)} if text classification has not been
+ * completed on the associated clip. This will be always be the case if the clip has not been
+ * copied to clipboard, or if there is no associated clip.
+ */
+ public static final int CLASSIFICATION_NOT_COMPLETE = 1;
+
+ /**
+ * Value returned by {@link #getConfidenceScore(String)} if text classification was not and will
+ * not be performed on the associated clip. This may be the case if the clip does not contain
+ * text in its first item, or if the text is too long.
+ */
+ public static final int CLASSIFICATION_NOT_PERFORMED = 2;
+
+ /**
+ * Value returned by {@link #getConfidenceScore(String)} if text classification has been
+ * completed.
+ */
+ public static final int CLASSIFICATION_COMPLETE = 3;
final CharSequence mLabel;
private final ArrayList<String> mMimeTypes;
private PersistableBundle mExtras;
private long mTimeStamp;
private boolean mIsStyledText;
+ private final ArrayMap<String, Float> mEntityConfidence = new ArrayMap<>();
+ private int mClassificationStatus = CLASSIFICATION_NOT_COMPLETE;
/**
* Create a new clip.
@@ -346,6 +382,61 @@ public class ClipDescription implements Parcelable {
mIsStyledText = isStyledText;
}
+ /**
+ * Sets the current status of text classification for the associated clip.
+ *
+ * @hide
+ */
+ public void setClassificationStatus(@ClassificationStatus int status) {
+ mClassificationStatus = status;
+ }
+
+ /**
+ * Returns a score indicating confidence that an instance of the given entity is present in the
+ * first item of the clip data, if that item is plain text and text classification has been
+ * performed. The value ranges from 0 (low confidence) to 1 (high confidence). 0 indicates that
+ * the entity was not found in the classified text.
+ *
+ * <p>Entities should be as defined in the {@link TextClassifier} class, such as
+ * {@link TextClassifier#TYPE_ADDRESS}, {@link TextClassifier#TYPE_URL}, or
+ * {@link TextClassifier#TYPE_EMAIL}.
+ *
+ * <p>If the result is positive for any entity, the full classification result as a
+ * {@link TextLinks} object may be obtained using the {@link ClipData.Item#getTextLinks()}
+ * method.
+ *
+ * @throws IllegalStateException if {@link #getClassificationStatus()} is not
+ * {@link #CLASSIFICATION_COMPLETE}
+ */
+ @FloatRange(from = 0.0, to = 1.0)
+ public float getConfidenceScore(@NonNull @TextClassifier.EntityType String entity) {
+ if (mClassificationStatus != CLASSIFICATION_COMPLETE) {
+ throw new IllegalStateException("Classification not complete");
+ }
+ return mEntityConfidence.getOrDefault(entity, 0f);
+ }
+
+ /**
+ * Returns {@link #CLASSIFICATION_COMPLETE} if text classification has been performed on the
+ * associated {@link ClipData}. If this is the case then {@link #getConfidenceScore} may be used
+ * to retrieve information about entities within the text. Otherwise, returns
+ * {@link #CLASSIFICATION_NOT_COMPLETE} if classification has not yet returned results, or
+ * {@link #CLASSIFICATION_NOT_PERFORMED} if classification was not attempted (e.g. because the
+ * text was too long).
+ */
+ public @ClassificationStatus int getClassificationStatus() {
+ return mClassificationStatus;
+ }
+
+ /**
+ * @hide
+ */
+ public void setConfidenceScores(Map<String, Float> confidences) {
+ mEntityConfidence.clear();
+ mEntityConfidence.putAll(confidences);
+ mClassificationStatus = CLASSIFICATION_COMPLETE;
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(128);
@@ -451,6 +542,23 @@ public class ClipDescription implements Parcelable {
dest.writePersistableBundle(mExtras);
dest.writeLong(mTimeStamp);
dest.writeBoolean(mIsStyledText);
+ dest.writeInt(mClassificationStatus);
+ dest.writeBundle(confidencesToBundle());
+ }
+
+ private Bundle confidencesToBundle() {
+ Bundle bundle = new Bundle();
+ int size = mEntityConfidence.size();
+ for (int i = 0; i < size; i++) {
+ bundle.putFloat(mEntityConfidence.keyAt(i), mEntityConfidence.valueAt(i));
+ }
+ return bundle;
+ }
+
+ private void readBundleToConfidences(Bundle bundle) {
+ for (String key : bundle.keySet()) {
+ mEntityConfidence.put(key, bundle.getFloat(key));
+ }
}
ClipDescription(Parcel in) {
@@ -459,6 +567,8 @@ public class ClipDescription implements Parcelable {
mExtras = in.readPersistableBundle();
mTimeStamp = in.readLong();
mIsStyledText = in.readBoolean();
+ mClassificationStatus = in.readInt();
+ readBundleToConfidences(in.readBundle());
}
public static final @android.annotation.NonNull Parcelable.Creator<ClipDescription> CREATOR =
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 49248b51a5c7..73b4f62a23e4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2621,6 +2621,48 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
return !TextUtils.isEmpty(uri.getUserInfo());
}
+ /**
+ * Returns the given content URI explicitly associated with the given {@link UserHandle}.
+ *
+ * @param contentUri The content URI to be associated with a user handle.
+ * @param userHandle The user handle with which to associate the URI.
+ *
+ * @throws IllegalArgumentException if
+ * <ul>
+ * <li>the given URI is not content URI (a content URI has {@link Uri#getScheme} equal to
+ * {@link ContentResolver.SCHEME_CONTENT}) or</li>
+ * <li>the given URI is already explicitly associated with a {@link UserHandle}, which is
+ * different than the given one.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static Uri createContentUriAsUser(
+ @NonNull Uri contentUri, @NonNull UserHandle userHandle) {
+ if (!ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
+ throw new IllegalArgumentException(String.format(
+ "Given URI [%s] is not a content URI: ", contentUri));
+ }
+
+ int userId = userHandle.getIdentifier();
+ if (uriHasUserId(contentUri)) {
+ if (String.valueOf(userId).equals(contentUri.getUserInfo())) {
+ return contentUri;
+ }
+ throw new IllegalArgumentException(String.format(
+ "Given URI [%s] already has a user ID, different from given user handle [%s]",
+ contentUri,
+ userId));
+ }
+
+ Uri.Builder builder = contentUri.buildUpon();
+ builder.encodedAuthority(
+ "" + userHandle.getIdentifier() + "@" + contentUri.getEncodedAuthority());
+ return builder.build();
+ }
+
/** @hide */
@UnsupportedAppUsage
public static Uri maybeAddUserId(Uri uri, int userId) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index df5c58c2634f..64ca92fa8132 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3531,6 +3531,7 @@ public abstract class Context {
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
+ PAC_PROXY_SERVICE,
VCN_MANAGEMENT_SERVICE,
//@hide: IP_MEMORY_STORE_SERVICE,
IPSEC_SERVICE,
@@ -3713,6 +3714,9 @@ public abstract class Context {
* usage statistics.
* <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")
* <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.
+ * <dt> {@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification")
+ * <dd> A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing
+ * web domain approval state.
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -3794,6 +3798,8 @@ public abstract class Context {
* @see android.app.usage.NetworkStatsManager
* @see android.os.HardwarePropertiesManager
* @see #HARDWARE_PROPERTIES_SERVICE
+ * @see #DOMAIN_VERIFICATION_SERVICE
+ * @see android.content.pm.verify.domain.DomainVerificationManager
*/
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
@@ -3813,7 +3819,8 @@ public abstract class Context {
* {@link android.view.inputmethod.InputMethodManager},
* {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
* {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
- * {@link android.app.usage.NetworkStatsManager}.
+ * {@link android.app.usage.NetworkStatsManager},
+ * {@link android.content.pm.verify.domain.DomainVerificationManager}.
* </p>
*
* <p>
@@ -3869,7 +3876,7 @@ public abstract class Context {
* @see #getSystemService(String)
* @hide
*/
- public static final String POWER_STATS_SERVICE = "power_stats";
+ public static final String POWER_STATS_SERVICE = "powerstats";
/**
* Use with {@link #getSystemService(String)} to retrieve a
@@ -4131,6 +4138,17 @@ public abstract class Context {
public static final String CONNECTIVITY_SERVICE = "connectivity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.net.PacProxyManager} for handling management of
+ * pac proxy information.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.PacProxyManager
+ * @hide
+ */
+ public static final String PAC_PROXY_SERVICE = "pac_proxy";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
* for managing Virtual Carrier Networks
*
@@ -4201,7 +4219,8 @@ public abstract class Context {
* @see #getSystemService(String)
* @hide
*/
- @TestApi public static final String TEST_NETWORK_SERVICE = "test_network";
+ @TestApi @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final String TEST_NETWORK_SERVICE = "test_network";
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link
@@ -4833,10 +4852,20 @@ public abstract class Context {
* @hide
*/
@TestApi
- @SuppressLint("ServiceName") // TODO: This should be renamed to POWER_WHITELIST_SERVICE
+ @Deprecated
+ @SuppressLint("ServiceName")
public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
/**
+ * System service name for the PowerExemptionManager.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @TestApi
+ public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.admin.DevicePolicyManager} for working with global
* device policy management.
@@ -5535,12 +5564,13 @@ public abstract class Context {
public static final String GAME_SERVICE = "game";
/**
- * Use with {@link #getSystemService(String)} to access domain verification service.
+ * Use with {@link #getSystemService(String)} to access
+ * {@link android.content.pm.verify.domain.DomainVerificationManager} to retrieve approval and
+ * user state for declared web domains.
*
* @see #getSystemService(String)
- * @hide
+ * @see android.content.pm.verify.domain.DomainVerificationManager
*/
- @SystemApi
public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index de17fda82d71..96b8fbe293f5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -28,6 +28,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.AppGlobals;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
@@ -1901,6 +1902,20 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.AUTO_REVOKE_PERMISSIONS";
/**
+ * Activity action: Launch UI to manage unused apps (hibernated apps).
+ *
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_UNUSED_APPS =
+ "android.intent.action.MANAGE_UNUSED_APPS";
+
+ /**
* Activity action: Launch UI to review permissions for an app.
* The system uses this intent if permission review for apps not
* supporting the new runtime permissions model is enabled. In
@@ -1940,8 +1955,8 @@ public class Intent implements Parcelable, Cloneable {
/**
* Activity action: Launch UI to show information about the usage
- * of a given permission. This action would be handled by apps that
- * want to show details about how and why given permission is being
+ * of a given permission group. This action would be handled by apps that
+ * want to show details about how and why given permission group is being
* used.
* <p>
* <strong>Important:</strong>You must protect the activity that handles
@@ -1951,7 +1966,7 @@ public class Intent implements Parcelable, Cloneable {
* activities that are not properly protected.
*
* <p>
- * Input: {@code android.intent.extra.PERMISSION_NAME} specifies the permission
+ * Input: {@link android.Manifest.permission_group} specifies the permission group
* for which the launched UI would be targeted.
* </p>
* <p>
@@ -2172,6 +2187,29 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.REVIEW_PERMISSION_USAGE";
/**
+ * Activity action: Launch UI to review the timeline history of permissions.
+ * <p>
+ * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group name
+ * that will be displayed by the launched UI.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ * <p class="note">
+ * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission.
+ * </p>
+ *
+ * @see #EXTRA_PERMISSION_GROUP_NAME
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REVIEW_PERMISSION_HISTORY =
+ "android.intent.action.REVIEW_PERMISSION_HISTORY";
+
+ /**
* Activity action: Launch UI to review ongoing app uses of permissions.
* <p>
* Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent
@@ -2331,6 +2369,7 @@ public class Intent implements Parcelable, Cloneable {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
/**
* Alarm Changed Action: This is broadcast when the AlarmClock
@@ -3733,6 +3772,7 @@ public class Intent implements Parcelable, Cloneable {
* has just been stopped (which is no longer running).
* @hide
*/
+ @TestApi
public static final String ACTION_USER_STOPPED =
"android.intent.action.USER_STOPPED";
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 1c21b2aa73a5..47c333ceb931 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -168,6 +169,7 @@ public class SyncAdapterType implements Parcelable {
*
* @hide
*/
+ @TestApi
public @Nullable String getPackageName() {
return packageName;
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 1660c9d23002..feb58a30e519 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -21,6 +21,7 @@ import android.annotation.TestApi;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
@@ -254,7 +255,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @See {@link android.R.attr#maxAspectRatio}.
* @hide
*/
- public float maxAspectRatio;
+ private float mMaxAspectRatio;
/**
* Value indicating the minimum aspect ratio the activity supports.
@@ -263,7 +264,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @See {@link android.R.attr#minAspectRatio}.
* @hide
*/
- public float minAspectRatio;
+ private float mMinAspectRatio;
/**
* Indicates that the activity works well with size changes like display changing size.
@@ -892,22 +893,42 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
@ChangeId
@Disabled
- public static final long FORCE_RESIZE_APP = 174042936L; // number refers to buganizer id
+ @TestApi
+ public static final long FORCE_RESIZE_APP = 174042936L; // buganizer id
+
+ /**
+ * This change id forces the packages it is applied to to be non-resizable.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @TestApi
+ public static final long FORCE_NON_RESIZE_APP = 181136395L; // buganizer id
/**
* Return value for {@link #supportsSizeChanges()} indicating that this activity does not
- * support size changes.
+ * support size changes due to the android.supports_size_changes metadata flag either being
+ * unset or set to {@code false} on application or activity level.
+ *
* @hide
*/
- public static final int SIZE_CHANGES_UNSUPPORTED = 0;
+ public static final int SIZE_CHANGES_UNSUPPORTED_METADATA = 0;
+
+ /**
+ * Return value for {@link #supportsSizeChanges()} indicating that this activity has been
+ * overridden to not support size changes through the compat framework change id
+ * {@link #FORCE_NON_RESIZE_APP}.
+ * @hide
+ */
+ public static final int SIZE_CHANGES_UNSUPPORTED_OVERRIDE = 1;
/**
* Return value for {@link #supportsSizeChanges()} indicating that this activity supports size
- * changes due to the android.supports_size_changes metadata flag being set either on
- * application or on activity level.
+ * changes due to the android.supports_size_changes metadata flag being set to {@code true}
+ * either on application or activity level.
* @hide
*/
- public static final int SIZE_CHANGES_SUPPORTED_METADATA = 1;
+ public static final int SIZE_CHANGES_SUPPORTED_METADATA = 2;
/**
* Return value for {@link #supportsSizeChanges()} indicating that this activity has been
@@ -915,11 +936,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* {@link #FORCE_RESIZE_APP}.
* @hide
*/
- public static final int SIZE_CHANGES_SUPPORTED_OVERRIDE = 2;
+ public static final int SIZE_CHANGES_SUPPORTED_OVERRIDE = 3;
/** @hide */
@IntDef(prefix = { "SIZE_CHANGES_" }, value = {
- SIZE_CHANGES_UNSUPPORTED,
+ SIZE_CHANGES_UNSUPPORTED_METADATA,
+ SIZE_CHANGES_UNSUPPORTED_OVERRIDE,
SIZE_CHANGES_SUPPORTED_METADATA,
SIZE_CHANGES_SUPPORTED_OVERRIDE,
})
@@ -927,6 +949,57 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public @interface SizeChangesSupportMode {}
/**
+ * This change id is the gatekeeper for all treatments that force a given min aspect ratio.
+ * Enabling this change will allow the following min aspect ratio treatments to be applied:
+ * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
+ * OVERRIDE_MIN_ASPECT_RATIO_LARGE
+ *
+ * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's
+ * manifest will be overridden to the largest enabled aspect ratio treatment unless the app's
+ * manifest value is higher.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
+
+ /**
+ * This change id sets the activity's min aspect ratio to a medium value as defined by
+ * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // buganizer id
+
+ /** @hide Medium override aspect ratio, currently 3:2. */
+ @TestApi
+ public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 3 / 2f;
+
+ /**
+ * This change id sets the activity's min aspect ratio to a large value as defined by
+ * OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // buganizer id
+
+ /** @hide Large override aspect ratio, currently 16:9 */
+ @TestApi
+ public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f;
+
+ /**
* Convert Java change bits to native.
*
* @hide
@@ -1065,6 +1138,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
public WindowLayout windowLayout;
+ /**
+ * Attribution tags for finer grained calls if a {@android.content.Context#sendBroadcast(Intent,
+ * String)} is used with a permission.
+ * @hide
+ */
+ public String[] attributionTags;
+
public ActivityInfo() {
}
@@ -1090,9 +1170,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
requestedVrComponent = orig.requestedVrComponent;
rotationAnimation = orig.rotationAnimation;
colorMode = orig.colorMode;
- maxAspectRatio = orig.maxAspectRatio;
- minAspectRatio = orig.minAspectRatio;
+ mMaxAspectRatio = orig.mMaxAspectRatio;
+ mMinAspectRatio = orig.mMinAspectRatio;
supportsSizeChanges = orig.supportsSizeChanges;
+ attributionTags = orig.attributionTags;
}
/**
@@ -1120,7 +1201,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @hide
*/
public boolean hasFixedAspectRatio() {
- return maxAspectRatio != 0 || minAspectRatio != 0;
+ return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
/**
@@ -1213,6 +1294,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
@SizeChangesSupportMode
public int supportsSizeChanges() {
+ if (CompatChanges.isChangeEnabled(FORCE_NON_RESIZE_APP,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
+ }
+
if (supportsSizeChanges) {
return SIZE_CHANGES_SUPPORTED_METADATA;
}
@@ -1223,7 +1310,59 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
return SIZE_CHANGES_SUPPORTED_OVERRIDE;
}
- return SIZE_CHANGES_UNSUPPORTED;
+ return SIZE_CHANGES_UNSUPPORTED_METADATA;
+ }
+
+ /** @hide */
+ public void setMaxAspectRatio(float maxAspectRatio) {
+ this.mMaxAspectRatio = maxAspectRatio;
+ }
+
+ /** @hide */
+ public float getMaxAspectRatio() {
+ return mMaxAspectRatio;
+ }
+
+ /** @hide */
+ public void setMinAspectRatio(float minAspectRatio) {
+ this.mMinAspectRatio = minAspectRatio;
+ }
+
+ /**
+ * Returns the min aspect ratio of this activity.
+ *
+ * This takes into account the minimum aspect ratio as defined in the app's manifest and
+ * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO.
+ *
+ * In the rare cases where the manifest minimum aspect ratio is required, use
+ * {@code getManifestMinAspectRatio}.
+ * @hide
+ */
+ public float getMinAspectRatio() {
+ if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return mMinAspectRatio;
+ }
+
+ if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
+ }
+
+ if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
+ }
+
+ return mMinAspectRatio;
+ }
+
+ /** @hide */
+ public float getManifestMinAspectRatio() {
+ return mMinAspectRatio;
}
/** @hide */
@@ -1269,8 +1408,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
/** @hide */
public static String sizeChangesSupportModeToString(@SizeChangesSupportMode int mode) {
switch (mode) {
- case SIZE_CHANGES_UNSUPPORTED:
- return "SIZE_CHANGES_UNSUPPORTED";
+ case SIZE_CHANGES_UNSUPPORTED_METADATA:
+ return "SIZE_CHANGES_UNSUPPORTED_METADATA";
+ case SIZE_CHANGES_UNSUPPORTED_OVERRIDE:
+ return "SIZE_CHANGES_UNSUPPORTED_OVERRIDE";
case SIZE_CHANGES_SUPPORTED_METADATA:
return "SIZE_CHANGES_SUPPORTED_METADATA";
case SIZE_CHANGES_SUPPORTED_OVERRIDE:
@@ -1323,15 +1464,27 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
if (requestedVrComponent != null) {
pw.println(prefix + "requestedVrComponent=" + requestedVrComponent);
}
- if (maxAspectRatio != 0) {
- pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
+ if (getMaxAspectRatio() != 0) {
+ pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
}
- if (minAspectRatio != 0) {
- pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+ if (getMinAspectRatio() != 0) {
+ pw.println(prefix + "minAspectRatio=" + getMinAspectRatio());
+ if (getManifestMinAspectRatio() != getMinAspectRatio()) {
+ pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio());
+ }
}
if (supportsSizeChanges) {
pw.println(prefix + "supportsSizeChanges=true");
}
+ if (attributionTags != null && attributionTags.length > 0) {
+ StringBuilder tags = new StringBuilder();
+ tags.append(attributionTags[0]);
+ for (int i = 1; i < attributionTags.length; i++) {
+ tags.append(", ");
+ tags.append(attributionTags[i]);
+ }
+ pw.println(prefix + "attributionTags=[" + tags + "]");
+ }
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1374,9 +1527,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
dest.writeString8(requestedVrComponent);
dest.writeInt(rotationAnimation);
dest.writeInt(colorMode);
- dest.writeFloat(maxAspectRatio);
- dest.writeFloat(minAspectRatio);
+ dest.writeFloat(mMaxAspectRatio);
+ dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
+ dest.writeString8Array(attributionTags);
}
/**
@@ -1493,9 +1647,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
requestedVrComponent = source.readString8();
rotationAnimation = source.readInt();
colorMode = source.readInt();
- maxAspectRatio = source.readFloat();
- minAspectRatio = source.readFloat();
+ mMaxAspectRatio = source.readFloat();
+ mMinAspectRatio = source.readFloat();
supportsSizeChanges = source.readBoolean();
+ attributionTags = source.createString8Array();
}
/**
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index 745c39b460fa..79b70f2bd5ee 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -23,32 +23,34 @@ package android.content.pm;
oneway interface IDataLoaderStatusListener {
/** The DataLoader process died, binder disconnected or class destroyed. */
const int DATA_LOADER_DESTROYED = 0;
+ /** The system is in process of binding to the DataLoader. */
+ const int DATA_LOADER_BINDING = 1;
/** DataLoader process is running and bound to. */
- const int DATA_LOADER_BOUND = 1;
+ const int DATA_LOADER_BOUND = 2;
/** DataLoader has handled onCreate(). */
- const int DATA_LOADER_CREATED = 2;
+ const int DATA_LOADER_CREATED = 3;
/** DataLoader can receive missing pages and read pages notifications,
* and ready to provide data. */
- const int DATA_LOADER_STARTED = 3;
+ const int DATA_LOADER_STARTED = 4;
/** DataLoader no longer ready to provide data and is not receiving
* any notifications from IncFS. */
- const int DATA_LOADER_STOPPED = 4;
+ const int DATA_LOADER_STOPPED = 5;
/** DataLoader streamed everything necessary to continue installation. */
- const int DATA_LOADER_IMAGE_READY = 5;
+ const int DATA_LOADER_IMAGE_READY = 6;
/** Installation can't continue as DataLoader failed to stream necessary data. */
- const int DATA_LOADER_IMAGE_NOT_READY = 6;
+ const int DATA_LOADER_IMAGE_NOT_READY = 7;
/** DataLoader instance can't run at the moment, but might recover later.
* It's up to system to decide if the app is still usable. */
- const int DATA_LOADER_UNAVAILABLE = 7;
+ const int DATA_LOADER_UNAVAILABLE = 8;
/** DataLoader reports that this instance is invalid and can never be restored.
* Warning: this is a terminal status that data loader should use carefully and
* the system should almost never use - e.g. only if all recovery attempts
* fail and all retry limits are exceeded. */
- const int DATA_LOADER_UNRECOVERABLE = 8;
+ const int DATA_LOADER_UNRECOVERABLE = 9;
/** There are no known issues with the data stream. */
const int STREAM_HEALTHY = 0;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5e08399f8abb..5ff11240db72 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4890,8 +4890,8 @@ public class PackageParser {
info.maxRecents = target.info.maxRecents;
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
- info.maxAspectRatio = target.info.maxAspectRatio;
- info.minAspectRatio = target.info.minAspectRatio;
+ info.setMaxAspectRatio(target.info.getMaxAspectRatio());
+ info.setMinAspectRatio(target.info.getManifestMinAspectRatio());
info.supportsSizeChanges = target.info.supportsSizeChanges;
info.requestedVrComponent = target.info.requestedVrComponent;
@@ -8157,7 +8157,7 @@ public class PackageParser {
return;
}
- info.maxAspectRatio = maxAspectRatio;
+ info.setMaxAspectRatio(maxAspectRatio);
mHasMaxAspectRatio = true;
}
@@ -8173,7 +8173,7 @@ public class PackageParser {
return;
}
- info.minAspectRatio = minAspectRatio;
+ info.setMinAspectRatio(minAspectRatio);
mHasMinAspectRatio = true;
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index fe8e4d7b1bb2..6f07dd7a24e8 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,6 +19,7 @@ package android.content.pm;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -183,6 +184,17 @@ public class ResolveInfo implements Parcelable {
@SystemApi
public boolean handleAllWebDataURI;
+ /**
+ * Whether the resolved {@link IntentFilter} declares {@link Intent#CATEGORY_BROWSABLE} and is
+ * thus allowed to automatically resolve an {@link Intent} as it's assumed the action is safe
+ * for the user.
+ *
+ * Note that the above doesn't apply when this is the only result is returned in the candidate
+ * set, as the system will not prompt before opening the result. It only applies when there are
+ * multiple candidates.
+ */
+ private final boolean mAutoResolutionAllowed;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ComponentInfo getComponentInfo() {
@@ -364,8 +376,26 @@ public class ResolveInfo implements Parcelable {
&& INTENT_FORWARDER_ACTIVITY.equals(activityInfo.targetActivity);
}
+ /**
+ * @see #mAutoResolutionAllowed
+ * @hide
+ */
+ public boolean isAutoResolutionAllowed() {
+ return mAutoResolutionAllowed;
+ }
+
public ResolveInfo() {
targetUserId = UserHandle.USER_CURRENT;
+
+ // It's safer to assume that an unaware caller that constructs a ResolveInfo doesn't
+ // accidentally mark a result as auto resolveable.
+ mAutoResolutionAllowed = false;
+ }
+
+ /** @hide */
+ public ResolveInfo(boolean autoResolutionAllowed) {
+ targetUserId = UserHandle.USER_CURRENT;
+ mAutoResolutionAllowed = autoResolutionAllowed;
}
public ResolveInfo(ResolveInfo orig) {
@@ -386,6 +416,7 @@ public class ResolveInfo implements Parcelable {
system = orig.system;
targetUserId = orig.targetUserId;
handleAllWebDataURI = orig.handleAllWebDataURI;
+ mAutoResolutionAllowed = orig.mAutoResolutionAllowed;
isInstantAppAvailable = orig.isInstantAppAvailable;
}
@@ -450,6 +481,7 @@ public class ResolveInfo implements Parcelable {
dest.writeInt(noResourceId ? 1 : 0);
dest.writeInt(iconResourceId);
dest.writeInt(handleAllWebDataURI ? 1 : 0);
+ dest.writeInt(mAutoResolutionAllowed ? 1 : 0);
dest.writeInt(isInstantAppAvailable ? 1 : 0);
}
@@ -498,6 +530,7 @@ public class ResolveInfo implements Parcelable {
noResourceId = source.readInt() != 0;
iconResourceId = source.readInt();
handleAllWebDataURI = source.readInt() != 0;
+ mAutoResolutionAllowed = source.readInt() != 0;
isInstantAppAvailable = source.readInt() != 0;
}
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 0d5b33cd8672..8f9a0d79f33b 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -29,6 +29,7 @@ import android.util.Log;
import android.util.jar.StrictJarFile;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.security.VerityUtils;
import java.io.File;
import java.io.IOException;
@@ -76,7 +77,8 @@ public class DexMetadataHelper {
* Returns whether fs-verity is required to install a dex metadata
*/
public static boolean isFsVerityRequired() {
- return SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
+ return VerityUtils.isFsVeritySupported()
+ && SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
}
/**
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 9a84ded99c67..fdd2c2ab83e3 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -474,14 +474,15 @@ public class PackageInfoWithoutStateUtils {
ai.screenOrientation = a.getScreenOrientation();
ai.resizeMode = a.getResizeMode();
Float maxAspectRatio = a.getMaxAspectRatio();
- ai.maxAspectRatio = maxAspectRatio != null ? maxAspectRatio : 0f;
+ ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f);
Float minAspectRatio = a.getMinAspectRatio();
- ai.minAspectRatio = minAspectRatio != null ? minAspectRatio : 0f;
+ ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f);
ai.supportsSizeChanges = a.getSupportsSizeChanges();
ai.requestedVrComponent = a.getRequestedVrComponent();
ai.rotationAnimation = a.getRotationAnimation();
ai.colorMode = a.getColorMode();
ai.windowLayout = a.getWindowLayout();
+ ai.attributionTags = a.getAttributionTags();
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = a.getMetaData();
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 6f478accedd7..9285ccb3cf0c 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -82,6 +82,9 @@ public class ParsedActivity extends ParsedMainComponent {
@Nullable
ActivityInfo.WindowLayout windowLayout;
+ @Nullable
+ String[] attributionTags;
+
public ParsedActivity(ParsedActivity other) {
super(other);
this.theme = other.theme;
@@ -107,6 +110,7 @@ public class ParsedActivity extends ParsedMainComponent {
this.rotationAnimation = other.rotationAnimation;
this.colorMode = other.colorMode;
this.windowLayout = other.windowLayout;
+ this.attributionTags = other.attributionTags;
}
/**
@@ -172,6 +176,7 @@ public class ParsedActivity extends ParsedMainComponent {
alias.requestedVrComponent = target.requestedVrComponent;
alias.directBootAware = target.directBootAware;
alias.setProcessName(target.getProcessName());
+ alias.attributionTags = target.attributionTags;
return alias;
// Not all attributes from the target ParsedActivity are copied to the alias.
@@ -299,6 +304,7 @@ public class ParsedActivity extends ParsedMainComponent {
} else {
dest.writeBoolean(false);
}
+ dest.writeString8Array(this.attributionTags);
}
public ParsedActivity() {
@@ -332,6 +338,7 @@ public class ParsedActivity extends ParsedMainComponent {
if (in.readBoolean()) {
windowLayout = new ActivityInfo.WindowLayout(in);
}
+ this.attributionTags = in.createString8Array();
}
public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
@@ -445,4 +452,9 @@ public class ParsedActivity extends ParsedMainComponent {
public ActivityInfo.WindowLayout getWindowLayout() {
return windowLayout;
}
+
+ @Nullable
+ public String[] getAttributionTags() {
+ return attributionTags;
+ }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 0f4aa061b72d..d99c4109e5ad 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -210,6 +210,11 @@ public class ParsedActivityUtils {
pkg.setVisibleToInstantApps(true);
}
+ String attributionTags = sa.getString(R.styleable.AndroidManifestActivity_attributionTags);
+ if (attributionTags != null) {
+ activity.attributionTags = attributionTags.split("\\|");
+ }
+
return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
false /*isAlias*/, visibleToEphemeral, input,
R.styleable.AndroidManifestActivity_parentActivityName,
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
index 7c335b1d26dd..62277ef671e3 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -43,9 +43,50 @@ import java.util.UUID;
*/
@SystemApi
@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
- genEqualsHashCode = true)
+ genEqualsHashCode = true, genHiddenConstDefs = true)
public final class DomainVerificationInfo implements Parcelable {
+ // Implementation note: the following states are OUTPUT only. Any value that is synonymous with
+ // a value in DomainVerificationState must be the EXACT same integer, so that state
+ // transformation does not have to occur when sending input into the system, assuming that the
+ // system only accepts those synonymous values. The public API values declared here are only
+ // used when exiting the system server to prepare this data object for consumption by the
+ // verification agent. These constants should only be referenced inside public API classes.
+ // The server must use DomainVerificationState.
+
+ /**
+ * No response has been recorded by either the system or any verification agent.
+ */
+ public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+
+ /**
+ * The domain has been explicitly verified.
+ */
+ public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+
+ /**
+ * Indicates the host cannot be modified by the verification agent.
+ */
+ public static final int STATE_UNMODIFIABLE = 2;
+
+ /**
+ * Indicates the host can be modified by the verification agent and is not considered verified.
+ */
+ public static final int STATE_MODIFIABLE_UNVERIFIED = 3;
+
+ /**
+ * Indicates the host can be modified by the verification agent and is considered verified.
+ */
+ public static final int STATE_MODIFIABLE_VERIFIED = 4;
+
+ /**
+ * The first available custom response code. This and any greater integer, along with {@link
+ * #STATE_SUCCESS} are the only values settable by the verification agent. All custom values
+ * will be treated as if the domain is unverified.
+ */
+ public static final int STATE_FIRST_VERIFIER_DEFINED =
+ DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+
/**
* A domain verification ID for use in later API calls. This represents the snapshot of the
* domains for a package on device, and will be invalidated whenever the package changes.
@@ -74,16 +115,12 @@ public final class DomainVerificationInfo implements Parcelable {
/**
* Map of host names to their current state. State is an integer, which defaults to {@link
- * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
- * verification agent (the intended consumer of this API), which can be equal to {@link
- * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
- * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+ * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+ * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+ * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
* <p>
- * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
- * verification agent may be able to act on these reserved values, and this ability can be
- * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
- * the agent attempt to verify all domains that it can modify the state of, even if it does not
- * understand the meaning of those values.
+ * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+ * that the agent attempt to verify all domains that it can modify the state of.
*/
@NonNull
private final Map<String, Integer> mHostToStateMap;
@@ -112,6 +149,39 @@ public final class DomainVerificationInfo implements Parcelable {
//@formatter:off
+ /** @hide */
+ @android.annotation.IntDef(prefix = "STATE_", value = {
+ STATE_NO_RESPONSE,
+ STATE_SUCCESS,
+ STATE_UNMODIFIABLE,
+ STATE_MODIFIABLE_UNVERIFIED,
+ STATE_MODIFIABLE_VERIFIED,
+ STATE_FIRST_VERIFIER_DEFINED
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface State {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String stateToString(@State int value) {
+ switch (value) {
+ case STATE_NO_RESPONSE:
+ return "STATE_NO_RESPONSE";
+ case STATE_SUCCESS:
+ return "STATE_SUCCESS";
+ case STATE_UNMODIFIABLE:
+ return "STATE_UNMODIFIABLE";
+ case STATE_MODIFIABLE_UNVERIFIED:
+ return "STATE_MODIFIABLE_UNVERIFIED";
+ case STATE_MODIFIABLE_VERIFIED:
+ return "STATE_MODIFIABLE_VERIFIED";
+ case STATE_FIRST_VERIFIER_DEFINED:
+ return "STATE_FIRST_VERIFIER_DEFINED";
+ default: return Integer.toHexString(value);
+ }
+ }
+
/**
* Creates a new DomainVerificationInfo.
*
@@ -134,16 +204,12 @@ public final class DomainVerificationInfo implements Parcelable {
* The package name that this data corresponds to.
* @param hostToStateMap
* Map of host names to their current state. State is an integer, which defaults to {@link
- * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
- * verification agent (the intended consumer of this API), which can be equal to {@link
- * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
- * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+ * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+ * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+ * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
* <p>
- * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
- * verification agent may be able to act on these reserved values, and this ability can be
- * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
- * the agent attempt to verify all domains that it can modify the state of, even if it does not
- * understand the meaning of those values.
+ * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+ * that the agent attempt to verify all domains that it can modify the state of.
* @hide
*/
@DataClass.Generated.Member
@@ -195,16 +261,12 @@ public final class DomainVerificationInfo implements Parcelable {
/**
* Map of host names to their current state. State is an integer, which defaults to {@link
- * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
- * verification agent (the intended consumer of this API), which can be equal to {@link
- * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
- * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+ * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+ * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+ * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
* <p>
- * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
- * verification agent may be able to act on these reserved values, and this ability can be
- * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
- * the agent attempt to verify all domains that it can modify the state of, even if it does not
- * understand the meaning of those values.
+ * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+ * that the agent attempt to verify all domains that it can modify the state of.
*/
@DataClass.Generated.Member
public @NonNull Map<String,Integer> getHostToStateMap() {
@@ -320,10 +382,10 @@ public final class DomainVerificationInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1614721812023L,
+ time = 1615317187669L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
- inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+ inputSignatures = "public static final int STATE_NO_RESPONSE\npublic static final int STATE_SUCCESS\npublic static final int STATE_UNMODIFIABLE\npublic static final int STATE_MODIFIABLE_UNVERIFIED\npublic static final int STATE_MODIFIABLE_VERIFIED\npublic static final int STATE_FIRST_VERIFIER_DEFINED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index f7c81bcffda3..55e19f2727bf 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -60,154 +60,97 @@ public final class DomainVerificationManager {
"android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
/**
- * No response has been recorded by either the system or any verification agent.
+ * Default return code for when a method has succeeded.
*
* @hide
*/
@SystemApi
- public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+ public static final int STATUS_OK = 0;
/**
- * The verification agent has explicitly verified the domain at some point.
+ * The provided domain set ID was invalid, probably due to the package being updated between
+ * the initial request that provided the ID and the method call that used it. This usually
+ * means the work being processed by the verification agent is outdated and a new request
+ * should be scheduled, which should already be in progress as part of the
+ * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
*
* @hide
*/
@SystemApi
- public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+ public static final int ERROR_DOMAIN_SET_ID_INVALID = 1;
/**
- * The first available custom response code. This and any greater integer, along with {@link
- * #STATE_SUCCESS} are the only values settable by the verification agent. All values will be
- * treated as if the domain is unverified.
+ * The provided domain set ID was null. This is a developer error.
*
* @hide
*/
@SystemApi
- public static final int STATE_FIRST_VERIFIER_DEFINED =
- DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ public static final int ERROR_DOMAIN_SET_ID_NULL = 2;
/**
+ * The provided set of domains was null or empty. This is a developer error.
+ *
* @hide
*/
- @NonNull
- public static String stateToDebugString(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_NO_RESPONSE:
- return "none";
- case DomainVerificationState.STATE_SUCCESS:
- return "verified";
- case DomainVerificationState.STATE_APPROVED:
- return "approved";
- case DomainVerificationState.STATE_DENIED:
- return "denied";
- case DomainVerificationState.STATE_MIGRATED:
- return "migrated";
- case DomainVerificationState.STATE_RESTORED:
- return "restored";
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- return "legacy_failure";
- case DomainVerificationState.STATE_SYS_CONFIG:
- return "system_configured";
- default:
- return String.valueOf(state);
- }
- }
+ @SystemApi
+ public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3;
/**
- * Checks if a state considers the corresponding domain to be successfully verified. The domain
- * verification agent may use this to determine whether or not to re-verify a domain.
+ * The provided set of domains contains a domain not declared by the target package. This
+ * usually means the work being processed by the verification agent is outdated and a new
+ * request should be scheduled, which should already be in progress as part of the
+ * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
*
* @hide
*/
@SystemApi
- public static boolean isStateVerified(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_APPROVED:
- case DomainVerificationState.STATE_MIGRATED:
- case DomainVerificationState.STATE_RESTORED:
- case DomainVerificationState.STATE_SYS_CONFIG:
- return true;
- case DomainVerificationState.STATE_NO_RESPONSE:
- case DomainVerificationState.STATE_DENIED:
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- default:
- return false;
- }
- }
+ public static final int ERROR_UNKNOWN_DOMAIN = 4;
/**
- * Checks if a state is modifiable by the domain verification agent. This is useful as the
- * platform may add new state codes in newer versions, and older verification agents can use
- * this method to determine if a state can be changed without having to be aware of what the new
- * state means.
+ * The system was unable to select the domain for approval. This indicates another application
+ * has been granted a higher approval, usually through domain verification, and the target
+ * package is unable to override it.
*
* @hide
*/
@SystemApi
- public static boolean isStateModifiable(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_NO_RESPONSE:
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_MIGRATED:
- case DomainVerificationState.STATE_RESTORED:
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- return true;
- case DomainVerificationState.STATE_APPROVED:
- case DomainVerificationState.STATE_DENIED:
- case DomainVerificationState.STATE_SYS_CONFIG:
- return false;
- default:
- return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
- }
- }
+ public static final int ERROR_UNABLE_TO_APPROVE = 5;
/**
- * For determine re-verify policy. This is hidden from the domain verification agent so that no
- * behavior is made based on the result.
+ * The provided state code is incorrect. The domain verification agent is only allowed to
+ * assign {@link DomainVerificationInfo#STATE_SUCCESS} or error codes equal to or greater than
+ * {@link DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED}.
*
* @hide
*/
- public static boolean isStateDefault(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_NO_RESPONSE:
- case DomainVerificationState.STATE_MIGRATED:
- case DomainVerificationState.STATE_RESTORED:
- return true;
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_APPROVED:
- case DomainVerificationState.STATE_DENIED:
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- case DomainVerificationState.STATE_SYS_CONFIG:
- default:
- return false;
- }
- }
+ @SystemApi
+ public static final int ERROR_INVALID_STATE_CODE = 6;
/**
+ * Used to communicate through {@link ServiceSpecificException}. Should not be exposed as API.
+ *
* @hide
*/
- public static final int ERROR_INVALID_DOMAIN_SET = 1;
- /**
- * @hide
- */
- public static final int ERROR_NAME_NOT_FOUND = 2;
+ public static final int INTERNAL_ERROR_NAME_NOT_FOUND = 1;
/**
* @hide
*/
@IntDef(prefix = {"ERROR_"}, value = {
- ERROR_INVALID_DOMAIN_SET,
- ERROR_NAME_NOT_FOUND,
+ ERROR_DOMAIN_SET_ID_INVALID,
+ ERROR_DOMAIN_SET_ID_NULL,
+ ERROR_DOMAIN_SET_NULL_OR_EMPTY,
+ ERROR_UNKNOWN_DOMAIN,
+ ERROR_UNABLE_TO_APPROVE,
+ ERROR_INVALID_STATE_CODE
})
- private @interface Error {
+ public @interface Error {
}
private final Context mContext;
private final IDomainVerificationManager mDomainVerificationManager;
-
/**
* System service to access the domain verification APIs.
* <p>
@@ -289,27 +232,24 @@ public final class DomainVerificationManager {
* @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
* @param domains List of host names to change the state of.
* @param state See {@link DomainVerificationInfo#getHostToStateMap()}.
- * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
- * invalid. This usually means the work being processed by the
- * verification agent is outdated and a new request should be
- * scheduled, if one has not already been done as part of the
- * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
* @throws NameNotFoundException If the ID is known to be good, but the package is
* unavailable. This may be because the package is installed on
* a volume that is no longer mounted. This error is
* unrecoverable until the package is available again, and
* should not be re-tried except on a time scheduled basis.
+ * @return error code or {@link #STATUS_OK} if successful
+ *
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
- public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
- @DomainVerificationState.State int state) throws NameNotFoundException {
+ public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ int state) throws NameNotFoundException {
try {
- mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+ return mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
new DomainSet(domains), state);
} catch (Exception e) {
- Exception converted = rethrow(e, domainSetId);
+ Exception converted = rethrow(e, null);
if (converted instanceof NameNotFoundException) {
throw (NameNotFoundException) converted;
} else if (converted instanceof RuntimeException) {
@@ -338,7 +278,7 @@ public final class DomainVerificationManager {
mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
allowed, mContext.getUserId());
} catch (Exception e) {
- Exception converted = rethrow(e, packageName);
+ Exception converted = rethrow(e, null);
if (converted instanceof NameNotFoundException) {
throw (NameNotFoundException) converted;
} else if (converted instanceof RuntimeException) {
@@ -372,24 +312,24 @@ public final class DomainVerificationManager {
* @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
* @param domains The domains to toggle the state of.
* @param enabled Whether or not the app should automatically open the domains specified.
- * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
- * invalid.
* @throws NameNotFoundException If the ID is known to be good, but the package is
* unavailable. This may be because the package is installed on
* a volume that is no longer mounted. This error is
* unrecoverable until the package is available again, and
* should not be re-tried except on a time scheduled basis.
+ * @return error code or {@link #STATUS_OK} if successful
+ *
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
- public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled) throws NameNotFoundException {
try {
- mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
- new DomainSet(domains), enabled, mContext.getUserId());
+ return mDomainVerificationManager.setDomainVerificationUserSelection(
+ domainSetId.toString(), new DomainSet(domains), enabled, mContext.getUserId());
} catch (Exception e) {
- Exception converted = rethrow(e, domainSetId);
+ Exception converted = rethrow(e, null);
if (converted instanceof NameNotFoundException) {
throw (NameNotFoundException) converted;
} else if (converted instanceof RuntimeException) {
@@ -447,123 +387,22 @@ public final class DomainVerificationManager {
}
}
- private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
- return rethrow(exception, domainSetId, null);
- }
-
private Exception rethrow(Exception exception, @Nullable String packageName) {
- return rethrow(exception, null, packageName);
- }
-
- private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
- @Nullable String packageName) {
if (exception instanceof ServiceSpecificException) {
- int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+ int serviceSpecificErrorCode = ((ServiceSpecificException) exception).errorCode;
if (packageName == null) {
packageName = exception.getMessage();
}
- @Error int managerErrorCode = packedErrorCode & 0xFFFF;
- switch (managerErrorCode) {
- case ERROR_INVALID_DOMAIN_SET:
- int errorSpecificCode = packedErrorCode >> 16;
- return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
- domainSetId, packageName, errorSpecificCode));
- case ERROR_NAME_NOT_FOUND:
- return new NameNotFoundException(packageName);
- default:
- return exception;
+ if (serviceSpecificErrorCode == INTERNAL_ERROR_NAME_NOT_FOUND) {
+ return new NameNotFoundException(packageName);
}
+
+ return exception;
} else if (exception instanceof RemoteException) {
return ((RemoteException) exception).rethrowFromSystemServer();
} else {
return exception;
}
}
-
- /**
- * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
- * provided by the caller is no longer valid. This may be recoverable, and the caller should
- * re-query the package name associated with the ID using
- * {@link #getDomainVerificationInfo(String)}
- * in order to check. If that also fails, then the package is no longer known to the device and
- * thus all pending work for it should be dropped.
- *
- * @hide
- */
- public static class InvalidDomainSetException extends IllegalArgumentException {
-
- public static final int REASON_ID_NULL = 1;
- public static final int REASON_ID_INVALID = 2;
- public static final int REASON_SET_NULL_OR_EMPTY = 3;
- public static final int REASON_UNKNOWN_DOMAIN = 4;
- public static final int REASON_UNABLE_TO_APPROVE = 5;
-
- /**
- * @hide
- */
- @IntDef({
- REASON_ID_NULL,
- REASON_ID_INVALID,
- REASON_SET_NULL_OR_EMPTY,
- REASON_UNKNOWN_DOMAIN,
- REASON_UNABLE_TO_APPROVE
- })
- public @interface Reason {
- }
-
- public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName,
- @Reason int reason) {
- switch (reason) {
- case REASON_ID_NULL:
- return "Domain set ID cannot be null";
- case REASON_ID_INVALID:
- return "Domain set ID " + domainSetId + " has been invalidated";
- case REASON_SET_NULL_OR_EMPTY:
- return "Domain set cannot be null or empty";
- case REASON_UNKNOWN_DOMAIN:
- return "Domain set contains value that was not declared by the target package "
- + packageName;
- case REASON_UNABLE_TO_APPROVE:
- return "Domain set contains value that was owned by another package";
- default:
- return "Unknown failure";
- }
- }
-
- @Reason
- private final int mReason;
-
- @Nullable
- private final UUID mDomainSetId;
-
- @Nullable
- private final String mPackageName;
-
- /**
- * @hide
- */
- public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
- @Reason int reason) {
- super(buildMessage(domainSetId, packageName, reason));
- mDomainSetId = domainSetId;
- mPackageName = packageName;
- mReason = reason;
- }
-
- @Nullable
- public UUID getDomainSetId() {
- return mDomainSetId;
- }
-
- @Nullable
- public String getPackageName() {
- return mPackageName;
- }
-
- @Reason
- public int getReason() {
- return mReason;
- }
- }
}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
index 17593ef2aeb1..8e28042bf581 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationState.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
@@ -17,15 +17,13 @@
package android.content.pm.verify.domain;
import android.annotation.IntDef;
+import android.annotation.NonNull;
/**
* @hide
*/
public interface DomainVerificationState {
- /**
- * @hide
- */
@IntDef({
STATE_NO_RESPONSE,
STATE_SUCCESS,
@@ -42,12 +40,12 @@ public interface DomainVerificationState {
// TODO(b/159952358): Document all the places that states need to be updated when one is added
/**
- * @see DomainVerificationManager#STATE_NO_RESPONSE
+ * @see DomainVerificationInfo#STATE_NO_RESPONSE
*/
int STATE_NO_RESPONSE = 0;
/**
- * @see DomainVerificationManager#STATE_SUCCESS
+ * @see DomainVerificationInfo#STATE_SUCCESS
*/
int STATE_SUCCESS = 1;
@@ -94,7 +92,132 @@ public interface DomainVerificationState {
int STATE_SYS_CONFIG = 7;
/**
- * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED
+ * @see DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED
*/
int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000;
+
+ @NonNull
+ static String stateToDebugString(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ return "none";
+ case DomainVerificationState.STATE_SUCCESS:
+ return "verified";
+ case DomainVerificationState.STATE_APPROVED:
+ return "approved";
+ case DomainVerificationState.STATE_DENIED:
+ return "denied";
+ case DomainVerificationState.STATE_MIGRATED:
+ return "migrated";
+ case DomainVerificationState.STATE_RESTORED:
+ return "restored";
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ return "legacy_failure";
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return "system_configured";
+ default:
+ return String.valueOf(state);
+ }
+ }
+
+ /**
+ * For determining re-verify policy. This is hidden from the domain verification agent so that
+ * no behavior is made based on the result.
+ */
+ static boolean isDefault(@State int state) {
+ switch (state) {
+ case STATE_NO_RESPONSE:
+ case STATE_MIGRATED:
+ case STATE_RESTORED:
+ return true;
+ case STATE_SUCCESS:
+ case STATE_APPROVED:
+ case STATE_DENIED:
+ case STATE_LEGACY_FAILURE:
+ case STATE_SYS_CONFIG:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a state considers the corresponding domain to be successfully verified. The domain
+ * verification agent may use this to determine whether or not to re-verify a domain.
+ */
+ static boolean isVerified(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return true;
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a state is modifiable by the domain verification agent. This is useful as the
+ * platform may add new state codes in newer versions, and older verification agents can use
+ * this method to determine if a state can be changed without having to be aware of what the new
+ * state means.
+ */
+ static boolean isModifiable(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ return true;
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return false;
+ default:
+ return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ }
+ }
+
+ /**
+ * Whether the state is migrated when updating a package. Generally this is only for states
+ * that maintain verification state or were set by an explicit user or developer action.
+ */
+ static boolean shouldMigrate(@State int state) {
+ switch (state) {
+ case STATE_SUCCESS:
+ case STATE_MIGRATED:
+ case STATE_RESTORED:
+ case STATE_APPROVED:
+ case STATE_DENIED:
+ return true;
+ case STATE_NO_RESPONSE:
+ case STATE_LEGACY_FAILURE:
+ case STATE_SYS_CONFIG:
+ case STATE_FIRST_VERIFIER_DEFINED:
+ default:
+ return false;
+ }
+ }
+
+ @DomainVerificationInfo.State
+ static int convertToInfoState(@State int internalState) {
+ if (internalState >= STATE_FIRST_VERIFIER_DEFINED) {
+ return internalState;
+ } else if (internalState == STATE_NO_RESPONSE) {
+ return DomainVerificationInfo.STATE_NO_RESPONSE;
+ } else if (internalState == STATE_SUCCESS) {
+ return DomainVerificationInfo.STATE_SUCCESS;
+ } else if (!isModifiable(internalState)) {
+ return DomainVerificationInfo.STATE_UNMODIFIABLE;
+ } else if (isVerified(internalState)) {
+ return DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED;
+ } else {
+ return DomainVerificationInfo.STATE_MODIFIABLE_UNVERIFIED;
+ }
+ }
}
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
index 332b92544581..53205f3ea470 100644
--- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -40,10 +40,10 @@ interface IDomainVerificationManager {
@nullable
List<DomainOwner> getOwnersForDomain(String domain, int userId);
- void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
+ int setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
- void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
+ int setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
boolean enabled, int userId);
}
diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java
index e24aeb2af3a1..8fa235294451 100644
--- a/core/java/android/ddm/DdmHandleHeap.java
+++ b/core/java/android/ddm/DdmHandleHeap.java
@@ -30,15 +30,7 @@ import java.nio.ByteBuffer;
*/
public class DdmHandleHeap extends ChunkHandler {
- public static final int CHUNK_HPIF = type("HPIF");
- public static final int CHUNK_HPSG = type("HPSG");
- public static final int CHUNK_HPDU = type("HPDU");
- public static final int CHUNK_HPDS = type("HPDS");
- public static final int CHUNK_NHSG = type("NHSG");
public static final int CHUNK_HPGC = type("HPGC");
- public static final int CHUNK_REAE = type("REAE");
- public static final int CHUNK_REAQ = type("REAQ");
- public static final int CHUNK_REAL = type("REAL");
private static DdmHandleHeap mInstance = new DdmHandleHeap();
@@ -50,15 +42,7 @@ public class DdmHandleHeap extends ChunkHandler {
* Register for the messages we're interested in.
*/
public static void register() {
- DdmServer.registerHandler(CHUNK_HPIF, mInstance);
- DdmServer.registerHandler(CHUNK_HPSG, mInstance);
- DdmServer.registerHandler(CHUNK_HPDU, mInstance);
- DdmServer.registerHandler(CHUNK_HPDS, mInstance);
- DdmServer.registerHandler(CHUNK_NHSG, mInstance);
DdmServer.registerHandler(CHUNK_HPGC, mInstance);
- DdmServer.registerHandler(CHUNK_REAE, mInstance);
- DdmServer.registerHandler(CHUNK_REAQ, mInstance);
- DdmServer.registerHandler(CHUNK_REAL, mInstance);
}
/**
@@ -81,24 +65,8 @@ public class DdmHandleHeap extends ChunkHandler {
Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
int type = request.type;
- if (type == CHUNK_HPIF) {
- return handleHPIF(request);
- } else if (type == CHUNK_HPSG) {
- return handleHPSGNHSG(request, false);
- } else if (type == CHUNK_HPDU) {
- return handleHPDU(request);
- } else if (type == CHUNK_HPDS) {
- return handleHPDS(request);
- } else if (type == CHUNK_NHSG) {
- return handleHPSGNHSG(request, true);
- } else if (type == CHUNK_HPGC) {
+ if (type == CHUNK_HPGC) {
return handleHPGC(request);
- } else if (type == CHUNK_REAE) {
- return handleREAE(request);
- } else if (type == CHUNK_REAQ) {
- return handleREAQ(request);
- } else if (type == CHUNK_REAL) {
- return handleREAL(request);
} else {
throw new RuntimeException("Unknown packet "
+ ChunkHandler.name(type));
@@ -106,112 +74,6 @@ public class DdmHandleHeap extends ChunkHandler {
}
/*
- * Handle a "HeaP InFo" request.
- */
- private Chunk handleHPIF(Chunk request) {
- ByteBuffer in = wrapChunk(request);
-
- int when = in.get();
- if (false)
- Log.v("ddm-heap", "Heap segment enable: when=" + when);
-
- boolean ok = DdmVmInternal.heapInfoNotify(when);
- if (!ok) {
- return createFailChunk(1, "Unsupported HPIF what");
- } else {
- return null; // empty response
- }
- }
-
- /*
- * Handle a "HeaP SeGment" or "Native Heap SeGment" request.
- */
- private Chunk handleHPSGNHSG(Chunk request, boolean isNative) {
- ByteBuffer in = wrapChunk(request);
-
- int when = in.get();
- int what = in.get();
- if (false)
- Log.v("ddm-heap", "Heap segment enable: when=" + when
- + ", what=" + what + ", isNative=" + isNative);
-
- boolean ok = DdmVmInternal.heapSegmentNotify(when, what, isNative);
- if (!ok) {
- return createFailChunk(1, "Unsupported HPSG what/when");
- } else {
- // TODO: if "when" is non-zero and we want to see a dump
- // right away, initiate a GC.
- return null; // empty response
- }
- }
-
- /*
- * Handle a "HeaP DUmp" request.
- *
- * This currently just returns a result code. We could pull up
- * the entire contents of the file and return them, but hprof dump
- * files can be a few megabytes.
- */
- private Chunk handleHPDU(Chunk request) {
- ByteBuffer in = wrapChunk(request);
- byte result;
-
- /* get the filename for the output file */
- int len = in.getInt();
- String fileName = getString(in, len);
- if (false)
- Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
-
- try {
- Debug.dumpHprofData(fileName);
- result = 0;
- } catch (UnsupportedOperationException uoe) {
- Log.w("ddm-heap", "hprof dumps not supported in this VM");
- result = -1;
- } catch (IOException ioe) {
- result = -1;
- } catch (RuntimeException re) {
- result = -1;
- }
-
- /* create a non-empty reply so the handler fires on completion */
- byte[] reply = { result };
- return new Chunk(CHUNK_HPDU, reply, 0, reply.length);
- }
-
- /*
- * Handle a "HeaP Dump Streaming" request.
- *
- * This tells the VM to create a heap dump and send it directly to
- * DDMS. The dumps are large enough that we don't want to copy the
- * data into a byte[] and send it from here.
- */
- private Chunk handleHPDS(Chunk request) {
- ByteBuffer in = wrapChunk(request);
- byte result;
-
- /* get the filename for the output file */
- if (false)
- Log.d("ddm-heap", "Heap dump: [DDMS]");
-
- String failMsg = null;
- try {
- Debug.dumpHprofDataDdms();
- } catch (UnsupportedOperationException uoe) {
- failMsg = "hprof dumps not supported in this VM";
- } catch (RuntimeException re) {
- failMsg = "Exception: " + re.getMessage();
- }
-
- if (failMsg != null) {
- Log.w("ddm-heap", failMsg);
- return createFailChunk(1, failMsg);
- } else {
- return null;
- }
- }
-
- /*
* Handle a "HeaP Garbage Collection" request.
*/
private Chunk handleHPGC(Chunk request) {
@@ -223,47 +85,4 @@ public class DdmHandleHeap extends ChunkHandler {
return null; // empty response
}
-
- /*
- * Handle a "REcent Allocation Enable" request.
- */
- private Chunk handleREAE(Chunk request) {
- ByteBuffer in = wrapChunk(request);
- boolean enable;
-
- enable = (in.get() != 0);
-
- if (false)
- Log.d("ddm-heap", "Recent allocation enable request: " + enable);
-
- DdmVmInternal.enableRecentAllocations(enable);
-
- return null; // empty response
- }
-
- /*
- * Handle a "REcent Allocation Query" request.
- */
- private Chunk handleREAQ(Chunk request) {
- //ByteBuffer in = wrapChunk(request);
-
- byte[] reply = new byte[1];
- reply[0] = DdmVmInternal.getRecentAllocationStatus() ? (byte)1 :(byte)0;
- return new Chunk(CHUNK_REAQ, reply, 0, reply.length);
- }
-
- /*
- * Handle a "REcent ALlocations" request.
- */
- private Chunk handleREAL(Chunk request) {
- //ByteBuffer in = wrapChunk(request);
-
- if (false)
- Log.d("ddm-heap", "Recent allocations request");
-
- /* generate the reply in a ready-to-go format */
- byte[] reply = DdmVmInternal.getRecentAllocations();
- return new Chunk(CHUNK_REAL, reply, 0, reply.length);
- }
}
-
diff --git a/core/java/android/ddm/DdmHandleThread.java b/core/java/android/ddm/DdmHandleThread.java
deleted file mode 100644
index 613ab75f9c1b..000000000000
--- a/core/java/android/ddm/DdmHandleThread.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ddm;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
-import android.util.Log;
-import java.nio.ByteBuffer;
-
-/**
- * Handle thread-related traffic.
- */
-public class DdmHandleThread extends ChunkHandler {
-
- public static final int CHUNK_THEN = type("THEN");
- public static final int CHUNK_THCR = type("THCR");
- public static final int CHUNK_THDE = type("THDE");
- public static final int CHUNK_THST = type("THST");
- public static final int CHUNK_STKL = type("STKL");
-
- private static DdmHandleThread mInstance = new DdmHandleThread();
-
-
- /* singleton, do not instantiate */
- private DdmHandleThread() {}
-
- /**
- * Register for the messages we're interested in.
- */
- public static void register() {
- DdmServer.registerHandler(CHUNK_THEN, mInstance);
- DdmServer.registerHandler(CHUNK_THST, mInstance);
- DdmServer.registerHandler(CHUNK_STKL, mInstance);
- }
-
- /**
- * Called when the DDM server connects. The handler is allowed to
- * send messages to the server.
- */
- public void connected() {}
-
- /**
- * Called when the DDM server disconnects. Can be used to disable
- * periodic transmissions or clean up saved state.
- */
- public void disconnected() {}
-
- /**
- * Handle a chunk of data.
- */
- public Chunk handleChunk(Chunk request) {
- if (false)
- Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
- int type = request.type;
-
- if (type == CHUNK_THEN) {
- return handleTHEN(request);
- } else if (type == CHUNK_THST) {
- return handleTHST(request);
- } else if (type == CHUNK_STKL) {
- return handleSTKL(request);
- } else {
- throw new RuntimeException("Unknown packet "
- + ChunkHandler.name(type));
- }
- }
-
- /*
- * Handle a "THread notification ENable" request.
- */
- private Chunk handleTHEN(Chunk request) {
- ByteBuffer in = wrapChunk(request);
-
- boolean enable = (in.get() != 0);
- //Log.i("ddm-thread", "Thread notify enable: " + enable);
-
- DdmVmInternal.threadNotify(enable);
- return null; // empty response
- }
-
- /*
- * Handle a "THread STatus" request. This is constructed by the VM.
- */
- private Chunk handleTHST(Chunk request) {
- ByteBuffer in = wrapChunk(request);
- // currently nothing to read from "in"
-
- //Log.d("ddm-thread", "Thread status request");
-
- byte[] status = DdmVmInternal.getThreadStats();
- if (status != null)
- return new Chunk(CHUNK_THST, status, 0, status.length);
- else
- return createFailChunk(1, "Can't build THST chunk");
- }
-
- /*
- * Handle a STacK List request.
- *
- * This is done by threadId, which isn't great since those are
- * recycled. We need a thread serial ID. The Linux tid is an okay
- * answer as it's unlikely to recycle at the exact wrong moment.
- * However, we're using the short threadId in THST messages, so we
- * use them here for consistency. (One thought is to keep the current
- * thread ID in the low 16 bits and somehow serialize the top 16 bits.)
- */
- private Chunk handleSTKL(Chunk request) {
- ByteBuffer in = wrapChunk(request);
- int threadId;
-
- threadId = in.getInt();
-
- //Log.d("ddm-thread", "Stack list request " + threadId);
-
- StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId);
- if (trace == null) {
- return createFailChunk(1, "Stack trace unavailable");
- } else {
- return createStackChunk(trace, threadId);
- }
- }
-
- /*
- * Serialize a StackTraceElement[] into an STKL chunk.
- *
- * We include the threadId in the response so the other side doesn't have
- * to match up requests and responses as carefully.
- */
- private Chunk createStackChunk(StackTraceElement[] trace, int threadId) {
- int bufferSize = 0;
-
- bufferSize += 4; // version, flags, whatever
- bufferSize += 4; // thread ID
- bufferSize += 4; // frame count
- for (StackTraceElement elem : trace) {
- bufferSize += 4 + elem.getClassName().length() * 2;
- bufferSize += 4 + elem.getMethodName().length() * 2;
- bufferSize += 4;
- if (elem.getFileName() != null)
- bufferSize += elem.getFileName().length() * 2;
- bufferSize += 4; // line number
- }
-
- ByteBuffer out = ByteBuffer.allocate(bufferSize);
- out.putInt(0);
- out.putInt(threadId);
- out.putInt(trace.length);
- for (StackTraceElement elem : trace) {
- out.putInt(elem.getClassName().length());
- putString(out, elem.getClassName());
- out.putInt(elem.getMethodName().length());
- putString(out, elem.getMethodName());
- if (elem.getFileName() != null) {
- out.putInt(elem.getFileName().length());
- putString(out, elem.getFileName());
- } else {
- out.putInt(0);
- }
- out.putInt(elem.getLineNumber());
- }
-
- return new Chunk(CHUNK_STKL, out);
- }
-}
-
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index e0faa51a938e..ca1031287e3e 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -16,9 +16,10 @@
package android.ddm;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
import android.util.Log;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
/**
* Just a place to stick handler registrations, instead of scattering
* them around.
@@ -46,7 +47,6 @@ public class DdmRegister {
if (false)
Log.v("ddm", "Registering DDM message handlers");
DdmHandleHello.register();
- DdmHandleThread.register();
DdmHandleHeap.register();
DdmHandleNativeHeap.register();
DdmHandleProfiling.register();
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index 25758e9d9a61..8c7695ad5a5a 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -72,6 +72,45 @@ public final class FontFamilyUpdateRequest {
* A font family definition.
*/
public static final class FontFamily {
+
+ /**
+ * Builds a {@link FontFamily}.
+ */
+ public static final class Builder {
+ @NonNull private final String mName;
+ @NonNull private final List<Font> mFonts;
+
+ /**
+ * Constructs a {@link FontFamily.Builder}.
+ */
+ public Builder(@NonNull String name, @NonNull List<Font> fonts) {
+ Objects.requireNonNull(name);
+ Preconditions.checkStringNotEmpty(name);
+ Objects.requireNonNull(fonts);
+ Preconditions.checkCollectionElementsNotNull(fonts, "fonts");
+ Preconditions.checkCollectionNotEmpty(fonts, "fonts");
+ mName = name;
+ mFonts = new ArrayList<>(fonts);
+ }
+
+ /**
+ * Adds a {@link Font} to the builder.
+ *
+ * @return This builder object.
+ */
+ public @NonNull Builder addFont(@NonNull Font font) {
+ mFonts.add(font);
+ return this;
+ }
+
+ /**
+ * Builds a {@link FontFamily}.
+ */
+ public @NonNull FontFamily build() {
+ return new FontFamily(mName, mFonts);
+ }
+ }
+
@NonNull
private final String mName;
@NonNull
@@ -90,12 +129,7 @@ public final class FontFamilyUpdateRequest {
* @see android.graphics.Typeface#create(String, int)
* @see Font
*/
- public FontFamily(@NonNull String name, @NonNull List<Font> fonts) {
- Objects.requireNonNull(name);
- Preconditions.checkStringNotEmpty(name);
- Objects.requireNonNull(fonts);
- Preconditions.checkCollectionElementsNotNull(fonts, "fonts");
- Preconditions.checkCollectionNotEmpty(fonts, "fonts");
+ private FontFamily(@NonNull String name, @NonNull List<Font> fonts) {
mName = name;
mFonts = fonts;
}
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index 7bf692f1d318..fa2ccbc189ad 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -195,6 +195,7 @@ public class FontManager {
* @return The current font configuration. null if failed to fetch information from the system
* service.
*/
+ @RequiresPermission(Manifest.permission.UPDATE_FONTS)
public @NonNull FontConfig getFontConfig() {
try {
return mIFontManager.getFontConfig();
diff --git a/core/java/android/hardware/SensorPrivacyManagerInternal.java b/core/java/android/hardware/SensorPrivacyManagerInternal.java
new file mode 100644
index 000000000000..d12e9f8418bc
--- /dev/null
+++ b/core/java/android/hardware/SensorPrivacyManagerInternal.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * SensorPrivacyManager calls for within the system server
+ * @hide
+ */
+public abstract class SensorPrivacyManagerInternal {
+
+ /**
+ * A class implementing this interface can register to receive a callback when state changes.
+ */
+ public interface OnSensorPrivacyChangedListener {
+ /**
+ * The callback invoked when the state changes.
+ */
+ void onSensorPrivacyChanged(boolean enabled);
+ }
+
+ /**
+ * A class implementing this interface can register to receive a callback when state changes for
+ * any user.
+ */
+ public interface OnUserSensorPrivacyChangedListener {
+ /**
+ * The callback invoked when the state changes.
+ */
+ void onSensorPrivacyChanged(int userId, boolean enabled);
+ }
+
+ /**
+ * Get the individual sensor privacy state for a given user.
+ */
+ public abstract boolean isSensorPrivacyEnabled(int userId, int sensor);
+
+ /**
+ * Registers a new listener to receive notification when the state of sensor privacy
+ * changes.
+ */
+ public abstract void addSensorPrivacyListener(int userId, int sensor,
+ OnSensorPrivacyChangedListener listener);
+
+ /**
+ * Registers a new listener to receive notification when the state of sensor privacy
+ * changes for any user.
+ */
+ public abstract void addSensorPrivacyListenerForAllUsers(int sensor,
+ OnUserSensorPrivacyChangedListener listener);
+}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 788afe3bdb8e..365dea691489 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -65,7 +65,7 @@ public class SystemSensorManager extends SensorManager {
private static final int CAPPED_SAMPLING_RATE_LEVEL = SensorDirectChannel.RATE_NORMAL;
private static final String HIGH_SAMPLING_RATE_SENSORS_PERMISSION =
- "android.permisison.HIGH_SAMPLING_RATE_SENSORS";
+ "android.permission.HIGH_SAMPLING_RATE_SENSORS";
/**
* For apps targeting S and above, a SecurityException is thrown when they do not have
* HIGH_SAMPLING_RATE_SENSORS permission, run in debug mode, and request sampling rates that
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 1fdce5e773b1..acfad1354ccf 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -29,6 +29,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import android.os.IBinder;
import android.os.RemoteException;
import android.security.keystore.KeyProperties;
import android.util.Slog;
@@ -410,6 +411,36 @@ public class BiometricManager {
}
/**
+ * Requests all other biometric sensors to resetLockout. Note that this is a "time bound"
+ * See the {@link android.hardware.biometrics.fingerprint.ISession#resetLockout(int,
+ * HardwareAuthToken)} and {@link android.hardware.biometrics.face.ISession#resetLockout(int,
+ * HardwareAuthToken)} documentation for complete details.
+ *
+ * @param token A binder from the caller, for the service to linkToDeath
+ * @param opPackageName Caller's package name
+ * @param fromSensorId The originating sensor that just authenticated. Note that this MUST
+ * be a sensor that meets {@link Authenticators#BIOMETRIC_STRONG} strength.
+ * The strength will also be enforced on the BiometricService side.
+ * @param userId The user that authentication succeeded for, and also the user that resetLockout
+ * should be applied to.
+ * @param hardwareAuthToken A valid HAT generated upon successful biometric authentication. Note
+ * that it is not necessary for the HAT to contain a challenge.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
+ int userId, byte[] hardwareAuthToken) {
+ if (mService != null) {
+ try {
+ mService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId,
+ hardwareAuthToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Provides a localized string that may be used as the label for a button that invokes
* {@link BiometricPrompt}.
*
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 1472bb940be5..86df0994a222 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -69,6 +69,10 @@ interface IAuthService {
// land as SIDs, and are used during key generation.
long[] getAuthenticatorIds();
+ // See documentation in BiometricManager.
+ void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId,
+ in byte[] hardwareAuthToken);
+
// Provides a localized string that may be used as the label for a button that invokes
// BiometricPrompt.
CharSequence getButtonLabel(int userId, String opPackageName, int authenticators);
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 7639c5dd4d16..059bf2622b00 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -70,4 +70,8 @@ interface IBiometricAuthenticator {
// Gets the authenticator ID representing the current set of enrolled templates
long getAuthenticatorId(int callingUserId);
+
+ // Requests the sensor to reset its lockout state
+ void resetLockout(IBinder token, String opPackageName, int userId,
+ in byte[] hardwareAuthToken);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 6d8bf0fb5543..64b51183a170 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -74,6 +74,10 @@ interface IBiometricService {
// land as SIDs, and are used during key generation.
long[] getAuthenticatorIds(int callingUserId);
+ // See documentation in BiometricManager.
+ void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId,
+ in byte[] hardwareAuthToken);
+
int getCurrentStrength(int sensorId);
// Returns a bit field of the modality (or modalities) that are will be used for authentication.
diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
index 0b81c6c8cc25..909f456dd433 100644
--- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
@@ -31,23 +31,31 @@ public class SensorPropertiesInternal implements Parcelable {
public final int sensorId;
@SensorProperties.Strength public final int sensorStrength;
public final int maxEnrollmentsPerUser;
+ public final boolean resetLockoutRequiresHardwareAuthToken;
+ public final boolean resetLockoutRequiresChallenge;
public static SensorPropertiesInternal from(@NonNull SensorPropertiesInternal prop) {
return new SensorPropertiesInternal(prop.sensorId, prop.sensorStrength,
- prop.maxEnrollmentsPerUser);
+ prop.maxEnrollmentsPerUser, prop.resetLockoutRequiresHardwareAuthToken,
+ prop.resetLockoutRequiresChallenge);
}
protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength,
- int maxEnrollmentsPerUser) {
+ int maxEnrollmentsPerUser, boolean resetLockoutRequiresHardwareAuthToken,
+ boolean resetLockoutRequiresChallenge) {
this.sensorId = sensorId;
this.sensorStrength = sensorStrength;
this.maxEnrollmentsPerUser = maxEnrollmentsPerUser;
+ this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+ this.resetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
}
protected SensorPropertiesInternal(Parcel in) {
sensorId = in.readInt();
sensorStrength = in.readInt();
maxEnrollmentsPerUser = in.readInt();
+ resetLockoutRequiresHardwareAuthToken = in.readBoolean();
+ resetLockoutRequiresChallenge = in.readBoolean();
}
public static final Creator<SensorPropertiesInternal> CREATOR =
@@ -73,6 +81,8 @@ public class SensorPropertiesInternal implements Parcelable {
dest.writeInt(sensorId);
dest.writeInt(sensorStrength);
dest.writeInt(maxEnrollmentsPerUser);
+ dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
+ dest.writeBoolean(resetLockoutRequiresChallenge);
}
@Override
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
index c592f19bc45c..bb3d91dbc65c 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -131,18 +131,10 @@ public class MultiResolutionImageReader implements AutoCloseable {
* @see
* android.hardware.camera2.params.MultiResolutionStreamConfigurationMap
*/
- public static @NonNull MultiResolutionImageReader newInstance(
+ public MultiResolutionImageReader(
@NonNull Collection<MultiResolutionStreamInfo> streams,
@Format int format,
@IntRange(from = 1) int maxImages) {
- return new MultiResolutionImageReader(streams, format, maxImages);
- }
-
- /**
- * @hide
- */
- protected MultiResolutionImageReader(Collection<MultiResolutionStreamInfo> streams,
- int format, int maxImages) {
mFormat = format;
mMaxImages = maxImages;
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
index aa1d1d4aaa18..e2e61ad4d31a 100644
--- a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
@@ -17,6 +17,7 @@
package android.hardware.camera2.params;
import android.annotation.NonNull;
+import android.annotation.IntRange;
import java.util.Objects;
@@ -50,9 +51,22 @@ public class MultiResolutionStreamInfo {
* MultiResolutionStreamConfigurationMap#getOutputInfo} or {@link
* MultiResolutionStreamConfigurationMap#getInputInfo} to obtain them for a particular format
* instead.</p>
+ *
+ * @param streamWidth The width in pixels of the camera stream
+ * @param streamHeight The height in pixels of the camera stream
+ * @param physicalCameraId The physical camera Id the camera stream is associated with
+ * @throws IllegalArgumentException if the streamWidth or streamHeight is invalid (either zero
+ * or negative).
*/
- public MultiResolutionStreamInfo(int streamWidth, int streamHeight,
+ public MultiResolutionStreamInfo(@IntRange(from = 1) int streamWidth,
+ @IntRange(from = 1) int streamHeight,
@NonNull String physicalCameraId) {
+ if (streamWidth <= 0) {
+ throw new IllegalArgumentException("Invalid stream width " + streamWidth);
+ }
+ if (streamHeight <= 0) {
+ throw new IllegalArgumentException("Invalid stream height " + streamHeight);
+ }
mStreamWidth = streamWidth;
mStreamHeight = streamHeight;
mPhysicalCameraId = physicalCameraId;
@@ -61,14 +75,14 @@ public class MultiResolutionStreamInfo {
/**
* The width of this particular image buffer stream in pixels.
*/
- public int getWidth() {
+ public @IntRange(from = 1) int getWidth() {
return mStreamWidth;
}
/**
* The height of this particular image buffer stream in pixels.
*/
- public int getHeight() {
+ public @IntRange(from = 1) int getHeight() {
return mStreamHeight;
}
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 7dc1eaabdc9c..93e5a0ea18f3 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -52,7 +52,8 @@ public class AmbientDisplayConfiguration {
|| wakeDisplayGestureEnabled(user)
|| pickupGestureEnabled(user)
|| tapGestureEnabled(user)
- || doubleTapGestureEnabled(user);
+ || doubleTapGestureEnabled(user)
+ || quickPickupSensorEnabled(user);
}
/** {@hide} */
@@ -100,6 +101,13 @@ public class AmbientDisplayConfiguration {
}
/** {@hide} */
+ public boolean quickPickupSensorEnabled(int user) {
+ return boolSettingDefaultOff(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user)
+ && !TextUtils.isEmpty(quickPickupSensorType())
+ && !alwaysOnEnabled(user);
+ }
+
+ /** {@hide} */
public boolean wakeScreenGestureAvailable() {
return mContext.getResources()
.getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
@@ -143,6 +151,11 @@ public class AmbientDisplayConfiguration {
}
/** {@hide} */
+ public String quickPickupSensorType() {
+ return mContext.getResources().getString(R.string.config_quickPickupSensorType);
+ }
+
+ /** {@hide} */
public boolean pulseOnLongPressEnabled(int user) {
return pulseOnLongPressAvailable() && boolSettingDefaultOff(
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index a6c6b46d5b81..6b7d8c3dde49 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -47,6 +47,10 @@ public final class BrightnessChangeEvent implements Parcelable {
* @hide */
public final int userId;
+ /** The unique id of the screen on which the brightness was changed */
+ @NonNull
+ public final String uniqueDisplayId;
+
/** Lux values of recent sensor data */
public final float[] luxValues;
@@ -120,15 +124,16 @@ public final class BrightnessChangeEvent implements Parcelable {
/** @hide */
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
- int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
- float powerBrightnessFactor, boolean nightMode, int colorTemperature,
- boolean reduceBrightColors, int reduceBrightColorsStrength,
+ int userId, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps,
+ float batteryLevel, float powerBrightnessFactor, boolean nightMode,
+ int colorTemperature, boolean reduceBrightColors, int reduceBrightColorsStrength,
float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig,
boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
this.userId = userId;
+ this.uniqueDisplayId = uniqueDisplayId;
this.luxValues = luxValues;
this.luxTimestamps = luxTimestamps;
this.batteryLevel = batteryLevel;
@@ -151,6 +156,7 @@ public final class BrightnessChangeEvent implements Parcelable {
this.timeStamp = other.timeStamp;
this.packageName = redactPackage ? null : other.packageName;
this.userId = other.userId;
+ this.uniqueDisplayId = other.uniqueDisplayId;
this.luxValues = other.luxValues;
this.luxTimestamps = other.luxTimestamps;
this.batteryLevel = other.batteryLevel;
@@ -172,6 +178,7 @@ public final class BrightnessChangeEvent implements Parcelable {
timeStamp = source.readLong();
packageName = source.readString();
userId = source.readInt();
+ uniqueDisplayId = source.readString();
luxValues = source.createFloatArray();
luxTimestamps = source.createLongArray();
batteryLevel = source.readFloat();
@@ -209,6 +216,7 @@ public final class BrightnessChangeEvent implements Parcelable {
dest.writeLong(timeStamp);
dest.writeString(packageName);
dest.writeInt(userId);
+ dest.writeString(uniqueDisplayId);
dest.writeFloatArray(luxValues);
dest.writeLongArray(luxTimestamps);
dest.writeFloat(batteryLevel);
@@ -231,6 +239,7 @@ public final class BrightnessChangeEvent implements Parcelable {
private long mTimeStamp;
private String mPackageName;
private int mUserId;
+ private String mUniqueDisplayId;
private float[] mLuxValues;
private long[] mLuxTimestamps;
private float mBatteryLevel;
@@ -270,6 +279,12 @@ public final class BrightnessChangeEvent implements Parcelable {
return this;
}
+ /** {@see BrightnessChangeEvent#uniqueScreenId} */
+ public Builder setUniqueDisplayId(String uniqueId) {
+ mUniqueDisplayId = uniqueId;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#luxValues} */
public Builder setLuxValues(float[] luxValues) {
mLuxValues = luxValues;
@@ -354,11 +369,11 @@ public final class BrightnessChangeEvent implements Parcelable {
/** Builds a BrightnessChangeEvent */
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
- mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors,
- mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness,
- mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
- mColorSampleDuration);
+ mPackageName, mUserId, mUniqueDisplayId, mLuxValues, mLuxTimestamps,
+ mBatteryLevel, mPowerBrightnessFactor, mNightMode, mColorTemperature,
+ mReduceBrightColors, mReduceBrightColorsStrength, mReduceBrightColorsOffset,
+ mLastBrightness, mIsDefaultBrightnessConfig, mIsUserSetBrightness,
+ mColorValueBuckets, mColorSampleDuration);
}
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index bbf421da6b48..2c3e7f18a3ab 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -68,6 +68,9 @@ public final class DisplayManager {
* {@link #EXTRA_WIFI_DISPLAY_STATUS} extra.
* </p><p>
* This broadcast is only sent to registered receivers and can only be sent by the system.
+ * </p><p>
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is required to
+ * receive this broadcast.
* </p>
* @hide
*/
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 0256b7bc6de0..8dc8d5b60943 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -475,28 +475,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
- public void revokeChallenge() {
- final List<FaceSensorPropertiesInternal> faceSensorProperties =
- getSensorPropertiesInternal();
- if (faceSensorProperties.isEmpty()) {
- Slog.e(TAG, "No sensors during revokeChallenge");
- }
- revokeChallenge(faceSensorProperties.get(0).sensorId);
- }
-
- /**
- * Invalidates the current challenge.
- *
- * TODO(b/171335732): should take userId and challenge
- *
- * @hide
- */
- @RequiresPermission(MANAGE_BIOMETRIC)
- public void revokeChallenge(int sensorId) {
+ public void revokeChallenge(int sensorId, int userId, long challenge) {
if (mService != null) {
try {
- mService.revokeChallenge(mToken, sensorId, 0 /* userId */,
- mContext.getOpPackageName(), 0 /* challenge */);
+ mService.revokeChallenge(mToken, sensorId, userId,
+ mContext.getOpPackageName(), challenge);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
index b9c0d12de22b..34cbcb417e1b 100644
--- a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
+++ b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
@@ -41,8 +41,11 @@ public class FaceSensorPropertiesInternal extends SensorPropertiesInternal {
*/
public FaceSensorPropertiesInternal(int sensorId, @SensorProperties.Strength int strength,
int maxEnrollmentsPerUser, boolean supportsFaceDetection,
- boolean supportsSelfIllumination) {
- super(sensorId, strength, maxEnrollmentsPerUser);
+ boolean supportsSelfIllumination, boolean resetLockoutRequiresChallenge) {
+ // resetLockout is managed by the HAL and requires a HardwareAuthToken for all face
+ // HAL interfaces (IBiometricsFace@1.0 HIDL and IFace@1.0 AIDL).
+ super(sensorId, strength, maxEnrollmentsPerUser,
+ true /* resetLockoutRequiresHardwareAuthToken */, resetLockoutRequiresChallenge);
this.supportsFaceDetection = supportsFaceDetection;
this.supportsSelfIllumination = supportsSelfIllumination;
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fc795d8a0488..1c33b26dfa18 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -686,17 +686,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Revokes the current challenge.
- * @hide
- */
- @RequiresPermission(MANAGE_FINGERPRINT)
- public void revokeChallenge(int userId) {
- // On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1,
- // this parameter is ignored.
- revokeChallenge(userId, 0L);
- }
-
- /**
* Revokes the specified challenge.
* @hide
*/
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 51addc95ac79..adc61a744f89 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -36,12 +36,6 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
public final @FingerprintSensorProperties.SensorType int sensorType;
/**
- * IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
- * cannot be checked
- */
- public final boolean resetLockoutRequiresHardwareAuthToken;
-
- /**
* The location of the center of the sensor if applicable. For example, sensors of type
* {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
* distance in pixels, measured from the left edge of the screen.
@@ -68,9 +62,13 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY,
int sensorRadius) {
- super(sensorId, strength, maxEnrollmentsPerUser);
+ // IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not
+ // required as it can only be generated/attested/verified by TEE components.
+ // IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See
+ // the HAL interface for more details.
+ super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken,
+ false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
- this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
this.sensorLocationX = sensorLocationX;
this.sensorLocationY = sensorLocationY;
this.sensorRadius = sensorRadius;
@@ -98,9 +96,9 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
- super(sensorId, strength, maxEnrollmentsPerUser);
+ super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken,
+ false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
- this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
int[] props = context.getResources().getIntArray(
com.android.internal.R.array.config_udfps_sensor_props);
@@ -119,7 +117,6 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
protected FingerprintSensorPropertiesInternal(Parcel in) {
super(in);
sensorType = in.readInt();
- resetLockoutRequiresHardwareAuthToken = in.readBoolean();
sensorLocationX = in.readInt();
sensorLocationY = in.readInt();
sensorRadius = in.readInt();
@@ -147,7 +144,6 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(sensorType);
- dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
dest.writeInt(sensorLocationX);
dest.writeInt(sensorLocationY);
dest.writeInt(sensorRadius);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b90c72832d36..ad71f15f6e26 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -461,6 +461,7 @@ public final class HdmiControlManager {
* @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
* @hide
*/
+ @SystemApi
public static final int VOLUME_CONTROL_ENABLED = 1;
/**
* HDMI CEC disabled.
@@ -468,6 +469,7 @@ public final class HdmiControlManager {
* @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
* @hide
*/
+ @SystemApi
public static final int VOLUME_CONTROL_DISABLED = 0;
/**
* @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
@@ -486,12 +488,14 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED = 1;
/**
* TV Wake on One Touch Play disabled.
*
* @hide
*/
+ @SystemApi
public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED = 0;
/**
* @hide
@@ -509,12 +513,14 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1;
/**
* Not sending &lt;Standby&gt; on sleep.
*
* @hide
*/
+ @SystemApi
public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0;
/**
* @hide
@@ -759,6 +765,7 @@ public final class HdmiControlManager {
* @hide
* @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(int)
*/
+ @SystemApi
public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE =
"volume_control_enabled";
/**
@@ -767,6 +774,7 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
"tv_wake_on_one_touch_play";
/**
@@ -775,6 +783,7 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP =
"tv_send_standby_on_sleep";
/**
@@ -1259,6 +1268,7 @@ public final class HdmiControlManager {
* @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void setHdmiCecVolumeControlEnabled(
@VolumeControl int hdmiCecVolumeControlEnabled) {
@@ -1274,6 +1284,7 @@ public final class HdmiControlManager {
* Returns whether volume changes via HDMI CEC are enabled.
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
@VolumeControl
public int getHdmiCecVolumeControlEnabled() {
@@ -2155,6 +2166,7 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void setTvWakeOnOneTouchPlay(@NonNull @TvWakeOnOneTouchPlay int value) {
if (mService == null) {
@@ -2176,6 +2188,7 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
@NonNull
@TvWakeOnOneTouchPlay
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2199,6 +2212,7 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void setTvSendStandbyOnSleep(@NonNull @TvSendStandbyOnSleep int value) {
if (mService == null) {
@@ -2220,6 +2234,7 @@ public final class HdmiControlManager {
*
* @hide
*/
+ @SystemApi
@NonNull
@TvSendStandbyOnSleep
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index a65f36b14f13..eaa8bd403e24 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -60,7 +60,8 @@ public final class ContextHubManager {
private static final String TAG = "ContextHubManager";
/**
- * An extra of type int describing the client's authorization state.
+ * An extra containing an int from {@link AuthorizationState} describing the client's
+ * authorization state.
*/
public static final String EXTRA_CLIENT_AUTHORIZATION_STATE =
"android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
@@ -115,11 +116,9 @@ public final class ContextHubManager {
/**
* Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a
- * nanoapp. The {@link ContextHubClient} must perform any cleanup with the nanoapp as soon as
- * possible.
- *
- * Note that the time between this state event and {@link AUTHORIZATION_DENIED} must be enough
- * for the {@link ContextHubClient} to send at least one message to the nanoapp.
+ * nanoapp. After receiving this state event, the {@link ContextHubClient} has one minute to
+ * perform any cleanup with the nanoapp such that the nanoapp is no longer performing work on
+ * behalf of the {@link ContextHubClient}.
*/
public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1;
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index dfb1e996c55a..00c691379187 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -25,4 +25,5 @@ oneway interface INetworkPolicyListener {
void onUidPoliciesChanged(int uid, int uidPolicies);
void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes);
void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
+ void onBlockedReasonChanged(int uid, int oldBlockedReason, int newBlockedReason);
}
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/IOnCompleteListener.aidl
index 7979afc54f90..4bb89f6c89e4 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/core/java/android/net/IOnCompleteListener.aidl
@@ -18,6 +18,6 @@
package android.net;
/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
+oneway interface IOnCompleteListener {
void onComplete();
}
diff --git a/core/java/android/net/IPacProxyInstalledListener.aidl b/core/java/android/net/IPacProxyInstalledListener.aidl
new file mode 100644
index 000000000000..b1f946e02215
--- /dev/null
+++ b/core/java/android/net/IPacProxyInstalledListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.Network;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+oneway interface IPacProxyInstalledListener {
+ void onPacProxyInstalled(in Network network, in ProxyInfo proxy);
+}
diff --git a/core/java/android/net/IPacProxyManager.aidl b/core/java/android/net/IPacProxyManager.aidl
new file mode 100644
index 000000000000..8f65c56662ef
--- /dev/null
+++ b/core/java/android/net/IPacProxyManager.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IPacProxyInstalledListener;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+interface IPacProxyManager
+{
+ void addListener(IPacProxyInstalledListener listener);
+ void removeListener(IPacProxyInstalledListener listener);
+ void setCurrentProxyScriptUrl(in ProxyInfo proxyInfo);
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 664120698971..0070796db410 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -23,6 +23,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityManager;
@@ -44,6 +45,8 @@ import android.util.DebugUtils;
import android.util.Pair;
import android.util.Range;
+import com.android.internal.util.function.pooled.PooledLambda;
+
import com.google.android.collect.Sets;
import java.lang.annotation.Retention;
@@ -53,6 +56,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
/**
* Manager for creating and modifying network policy rules.
@@ -60,6 +64,7 @@ import java.util.concurrent.ConcurrentHashMap;
* @hide
*/
@TestApi
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.NETWORK_POLICY_SERVICE)
public class NetworkPolicyManager {
@@ -198,12 +203,157 @@ public class NetworkPolicyManager {
})
public @interface SubscriptionOverrideMask {}
+ /**
+ * Flag to indicate that an app is not subject to any restrictions that could result in its
+ * network access blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_REASON_NONE = 0;
+
+ /**
+ * Flag to indicate that an app is subject to Battery saver restrictions that would
+ * result in its network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_REASON_BATTERY_SAVER = 1 << 0;
+
+ /**
+ * Flag to indicate that an app is subject to Doze restrictions that would
+ * result in its network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_REASON_DOZE = 1 << 1;
+
+ /**
+ * Flag to indicate that an app is subject to App Standby restrictions that would
+ * result in its network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_REASON_APP_STANDBY = 1 << 2;
+
+ /**
+ * Flag to indicate that an app is subject to Restricted mode restrictions that would
+ * result in its network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3;
+
+ /**
+ * Flag to indicate that an app is subject to Data saver restrictions that would
+ * result in its metered network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_METERED_REASON_DATA_SAVER = 1 << 16;
+
+ /**
+ * Flag to indicate that an app is subject to user restrictions that would
+ * result in its metered network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 1 << 17;
+
+ /**
+ * Flag to indicate that an app is subject to Device admin restrictions that would
+ * result in its metered network access being blocked.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 1 << 18;
+
+ /** @hide */
+ public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000;
+
+ /**
+ * Flag to indicate that app is not exempt from any network restrictions.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_NONE = 0;
+ /**
+ * Flag to indicate that app is exempt from certain network restrictions because of it being a
+ * system component.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_SYSTEM = 1 << 0;
+ /**
+ * Flag to indicate that app is exempt from certain network restrictions because of it being
+ * in the foreground.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_FOREGROUND = 1 << 1;
+ /**
+ * Flag to indicate that app is exempt from certain network restrictions because of it being
+ * in the {@code allow-in-power-save} list.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_POWER_SAVE_ALLOWLIST = 1 << 2;
+ /**
+ * Flag to indicate that app is exempt from certain network restrictions because of it being
+ * in the {@code allow-in-power-save-except-idle} list.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST = 1 << 3;
+ /**
+ * Flag to indicate that app is exempt from certain network restrictions because of it holding
+ * certain privileged permissions.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS = 1 << 4;
+ /**
+ * Flag to indicate that app is exempt from certain metered network restrictions because user
+ * explicitly exempted it.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_METERED_REASON_USER_EXEMPTED = 1 << 16;
+
+ /** @hide */
+ public static final int ALLOWED_METERED_REASON_MASK = 0xffff0000;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"BLOCKED_"}, value = {
+ BLOCKED_REASON_NONE,
+ BLOCKED_REASON_BATTERY_SAVER,
+ BLOCKED_REASON_DOZE,
+ BLOCKED_REASON_APP_STANDBY,
+ BLOCKED_REASON_RESTRICTED_MODE,
+ BLOCKED_METERED_REASON_DATA_SAVER,
+ BLOCKED_METERED_REASON_USER_RESTRICTED,
+ BLOCKED_METERED_REASON_ADMIN_DISABLED,
+ })
+ public @interface BlockedReason {}
+
private final Context mContext;
@UnsupportedAppUsage
private INetworkPolicyManager mService;
private final Map<SubscriptionCallback, SubscriptionCallbackProxy>
- mCallbackMap = new ConcurrentHashMap<>();
+ mSubscriptionCallbackMap = new ConcurrentHashMap<>();
+ private final Map<NetworkPolicyCallback, NetworkPolicyCallbackProxy>
+ mNetworkPolicyCallbackMap = new ConcurrentHashMap<>();
/** @hide */
public NetworkPolicyManager(Context context, INetworkPolicyManager service) {
@@ -318,7 +468,7 @@ public class NetworkPolicyManager {
}
final SubscriptionCallbackProxy callbackProxy = new SubscriptionCallbackProxy(callback);
- if (null != mCallbackMap.putIfAbsent(callback, callbackProxy)) {
+ if (null != mSubscriptionCallbackMap.putIfAbsent(callback, callbackProxy)) {
throw new IllegalArgumentException("Callback is already registered.");
}
registerListener(callbackProxy);
@@ -331,7 +481,7 @@ public class NetworkPolicyManager {
throw new NullPointerException("Callback cannot be null.");
}
- final SubscriptionCallbackProxy callbackProxy = mCallbackMap.remove(callback);
+ final SubscriptionCallbackProxy callbackProxy = mSubscriptionCallbackMap.remove(callback);
if (callbackProxy == null) return;
unregisterListener(callbackProxy);
@@ -689,6 +839,142 @@ public class NetworkPolicyManager {
return WifiInfo.sanitizeSsid(ssid);
}
+ /**
+ * Returns whether network access of an UID is blocked or not based on {@code blockedReasons}
+ * corresponding to it.
+ *
+ * {@code blockedReasons} would be a bitwise {@code OR} combination of the
+ * {@code BLOCKED_REASON_*} and/or {@code BLOCKED_METERED_REASON_*} constants.
+ *
+ * @param blockedReasons Value indicating the reasons for why the network access of an UID is
+ * blocked. If the value is equal to {@link #BLOCKED_REASON_NONE}, then
+ * it indicates that an app's network access is not blocked.
+ * @param meteredNetwork Value indicating whether the network is metered or not.
+ * @return Whether network access is blocked or not.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static boolean isUidBlocked(@BlockedReason int blockedReasons, boolean meteredNetwork) {
+ if (blockedReasons == BLOCKED_REASON_NONE) {
+ return false;
+ }
+ final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK);
+ if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) {
+ return true;
+ }
+ if (meteredNetwork) {
+ return blockedReasons != BLOCKED_REASON_NONE;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the {@code string} representation of {@code blockedReasons} argument.
+ *
+ * @param blockedReasons Value indicating the reasons for why the network access of an UID is
+ * blocked.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @NonNull
+ public static String blockedReasonsToString(@BlockedReason int blockedReasons) {
+ return DebugUtils.flagsToString(NetworkPolicyManager.class, "BLOCKED_", blockedReasons);
+ }
+
+ /**
+ * Register a {@link NetworkPolicyCallback} to listen for changes to network blocked status
+ * of apps.
+ *
+ * Note that when a caller tries to register a new callback, it might replace a previously
+ * registered callback if it is considered equal to the new one, based on the
+ * {@link Object#equals(Object)} check.
+ *
+ * @param executor The {@link Executor} to run the callback on.
+ * @param callback The {@link NetworkPolicyCallback} to be registered.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
+ public void registerNetworkPolicyCallback(@Nullable Executor executor,
+ @NonNull NetworkPolicyCallback callback) {
+ if (callback == null) {
+ throw new NullPointerException("Callback cannot be null.");
+ }
+
+ final NetworkPolicyCallbackProxy callbackProxy = new NetworkPolicyCallbackProxy(
+ executor, callback);
+ registerListener(callbackProxy);
+ mNetworkPolicyCallbackMap.put(callback, callbackProxy);
+ }
+
+ /**
+ * Unregister a previously registered {@link NetworkPolicyCallback}.
+ *
+ * @param callback The {@link NetworkPolicyCallback} to be unregistered.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
+ public void unregisterNetworkPolicyCallback(@NonNull NetworkPolicyCallback callback) {
+ if (callback == null) {
+ throw new NullPointerException("Callback cannot be null.");
+ }
+
+ final NetworkPolicyCallbackProxy callbackProxy = mNetworkPolicyCallbackMap.remove(callback);
+ if (callbackProxy == null) return;
+ unregisterListener(callbackProxy);
+ }
+
+ /**
+ * Interface for the callback to listen for changes to network blocked status of apps.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public interface NetworkPolicyCallback {
+ /**
+ * Called when the reason for why the network access of an UID is blocked changes.
+ *
+ * @param uid The UID for which the blocked status changed.
+ * @param blockedReasons Value indicating the reasons for why the network access of an
+ * UID is blocked.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ default void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {}
+ }
+
+ /** @hide */
+ public static class NetworkPolicyCallbackProxy extends Listener {
+ private final Executor mExecutor;
+ private final NetworkPolicyCallback mCallback;
+
+ NetworkPolicyCallbackProxy(@Nullable Executor executor,
+ @NonNull NetworkPolicyCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onBlockedReasonChanged(int uid, @BlockedReason int oldBlockedReasons,
+ @BlockedReason int newBlockedReasons) {
+ if (oldBlockedReasons != newBlockedReasons) {
+ dispatchOnUidBlockedReasonChanged(mExecutor, mCallback, uid, newBlockedReasons);
+ }
+ }
+ }
+
+ private static void dispatchOnUidBlockedReasonChanged(@Nullable Executor executor,
+ @NonNull NetworkPolicyCallback callback, int uid, @BlockedReason int blockedReasons) {
+ if (executor == null) {
+ callback.onUidBlockedReasonChanged(uid, blockedReasons);
+ } else {
+ executor.execute(PooledLambda.obtainRunnable(
+ NetworkPolicyCallback::onUidBlockedReasonChanged,
+ callback, uid, blockedReasons).recycleOnUse());
+ }
+ }
+
/** @hide */
public static class SubscriptionCallback {
/**
@@ -743,5 +1029,7 @@ public class NetworkPolicyManager {
@Override public void onSubscriptionOverride(int subId, int overrideMask,
int overrideValue, int[] networkTypes) { }
@Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
+ @Override public void onBlockedReasonChanged(int uid,
+ int oldBlockedReasons, int newBlockedReasons) { }
}
}
diff --git a/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml b/core/java/android/net/NetworkScore.aidl
index 0435c301a2da..af12dcf7f17a 100644
--- a/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml
+++ b/core/java/android/net/NetworkScore.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
/**
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources>
- <!-- A list of apps to be allowed in the personal profile of an organization-owned device. -->
- <string-array translatable="false" name="vendor_allowed_personal_apps_org_owned_device">
- </string-array>
-</resources>
+
+package android.net;
+
+parcelable NetworkScore;
+
diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java
new file mode 100644
index 000000000000..f47801002296
--- /dev/null
+++ b/core/java/android/net/NetworkScore.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Object representing the quality of a network as perceived by the user.
+ *
+ * A NetworkScore object represents the characteristics of a network that affects how good the
+ * network is considered for a particular use.
+ * @hide
+ */
+// TODO : @SystemApi when the implementation is complete
+public final class NetworkScore implements Parcelable {
+ // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
+ // a migration.
+ private final int mLegacyInt;
+
+ /** @hide */
+ NetworkScore(final int legacyInt) {
+ this.mLegacyInt = legacyInt;
+ }
+
+ private NetworkScore(@NonNull final Parcel in) {
+ mLegacyInt = in.readInt();
+ }
+
+ public int getLegacyInt() {
+ return mLegacyInt;
+ }
+
+ @Override
+ public String toString() {
+ return "Score(" + mLegacyInt + ")";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+ dest.writeInt(mLegacyInt);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() {
+ @Override
+ @NonNull
+ public NetworkScore createFromParcel(@NonNull final Parcel in) {
+ return new NetworkScore(in);
+ }
+
+ @Override
+ @NonNull
+ public NetworkScore[] newArray(int size) {
+ return new NetworkScore[size];
+ }
+ };
+
+ /**
+ * A builder for NetworkScore.
+ */
+ public static final class Builder {
+ private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
+ private int mLegacyInt = INVALID_LEGACY_INT;
+
+ /**
+ * Sets the legacy int for this score.
+ *
+ * Do not rely on this. It will be gone by the time S is released.
+ *
+ * @param score the legacy int
+ * @return this
+ */
+ @NonNull
+ public Builder setLegacyInt(final int score) {
+ mLegacyInt = score;
+ return this;
+ }
+
+ /**
+ * Builds this NetworkScore.
+ * @return The built NetworkScore object.
+ */
+ @NonNull
+ public NetworkScore build() {
+ return new NetworkScore(mLegacyInt);
+ }
+ }
+}
diff --git a/core/java/android/net/PacProxyManager.java b/core/java/android/net/PacProxyManager.java
new file mode 100644
index 000000000000..8f7ad8c335ca
--- /dev/null
+++ b/core/java/android/net/PacProxyManager.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@SystemService(Context.PAC_PROXY_SERVICE)
+public class PacProxyManager {
+ private final Context mContext;
+ private final IPacProxyManager mService;
+ @GuardedBy("mListenerMap")
+ private final HashMap<PacProxyInstalledListener, PacProxyInstalledListenerProxy>
+ mListenerMap = new HashMap<>();
+
+ /** @hide */
+ public PacProxyManager(Context context, IPacProxyManager service) {
+ Objects.requireNonNull(service, "missing IPacProxyManager");
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Add a listener to start monitoring events reported by PacProxyService.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void addPacProxyInstalledListener(@NonNull Executor executor,
+ @NonNull PacProxyInstalledListener listener) {
+ try {
+ synchronized (mListenerMap) {
+ final PacProxyInstalledListenerProxy listenerProxy =
+ new PacProxyInstalledListenerProxy(executor, listener);
+
+ if (null != mListenerMap.putIfAbsent(listener, listenerProxy)) {
+ throw new IllegalStateException("Listener is already added.");
+ }
+ mService.addListener(listenerProxy);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove the listener to stop monitoring the event of PacProxyInstalledListener.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void removePacProxyInstalledListener(@NonNull PacProxyInstalledListener listener) {
+ try {
+ synchronized (mListenerMap) {
+ final PacProxyInstalledListenerProxy listenerProxy = mListenerMap.remove(listener);
+ if (listenerProxy == null) return;
+ mService.removeListener(listenerProxy);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Updates the PAC Proxy Installer with current Proxy information.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+ try {
+ mService.setCurrentProxyScriptUrl(proxy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * A callback interface for monitoring changes of PAC proxy information.
+ */
+ public interface PacProxyInstalledListener {
+ /**
+ * Notify that the PAC proxy has been installed. Note that this method will be called with
+ * a ProxyInfo with an empty PAC URL when the PAC proxy is removed.
+ *
+ * This method supports different PAC proxies per-network but not all devices might support
+ * per-network proxies. In that case it will be applied globally.
+ *
+ * @param network the network for which this proxy installed.
+ * @param proxy the installed proxy.
+ */
+ void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy);
+ }
+
+ /**
+ * PacProxyInstalledListener proxy for PacProxyInstalledListener object.
+ * @hide
+ */
+ public class PacProxyInstalledListenerProxy extends IPacProxyInstalledListener.Stub {
+ private final Executor mExecutor;
+ private final PacProxyInstalledListener mListener;
+
+ PacProxyInstalledListenerProxy(Executor executor, PacProxyInstalledListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onPacProxyInstalled(Network network, ProxyInfo proxy) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mListener.onPacProxyInstalled(network, proxy);
+ });
+ });
+ }
+ }
+}
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 326943a27d4e..84b7eecd2027 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -51,7 +51,7 @@ public class PacProxySelector extends ProxySelector {
ServiceManager.getService(PROXY_SERVICE));
if (mProxyService == null) {
// Added because of b10267814 where mako is restarting.
- Log.e(TAG, "PacProxyInstaller: no proxy service");
+ Log.e(TAG, "PacProxyService: no proxy service");
}
mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
}
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f472ed4381d1..77754d1256a7 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -16,12 +16,15 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
@@ -56,18 +59,21 @@ import java.util.List;
*/
public class VpnManager {
/** Type representing a lack of VPN @hide */
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_NONE = -1;
/**
* A VPN created by an app using the {@link VpnService} API.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_SERVICE = 1;
/**
* A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_PLATFORM = 2;
/**
@@ -76,6 +82,7 @@ public class VpnManager {
* @hide
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_LEGACY = 3;
/**
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 8ebf757760c3..062438c6e5db 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -73,7 +73,8 @@ import java.util.concurrent.Executor;
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ private static final Map<
+ VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
@NonNull private final Context mContext;
@@ -93,13 +94,13 @@ public class VcnManager {
}
/**
- * Get all currently registered VcnNetworkPolicyListeners for testing purposes.
+ * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes.
*
* @hide
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
- public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
getAllPolicyListeners() {
return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
}
@@ -162,14 +163,14 @@ public class VcnManager {
}
// TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using
- // the new VcnNetworkPolicyListener API
+ // the new VcnNetworkPolicyChangeListener API
/**
* VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
* can register to receive updates for VCN-underlying Network policies from the System Server.
*
* @hide
*/
- public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {}
+ public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {}
/**
* Add a listener for VCN-underlying network policy updates.
@@ -185,7 +186,7 @@ public class VcnManager {
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public void addVcnUnderlyingNetworkPolicyListener(
@NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
- addVcnNetworkPolicyListener(executor, listener);
+ addVcnNetworkPolicyChangeListener(executor, listener);
}
/**
@@ -198,7 +199,7 @@ public class VcnManager {
*/
public void removeVcnUnderlyingNetworkPolicyListener(
@NonNull VcnUnderlyingNetworkPolicyListener listener) {
- removeVcnNetworkPolicyListener(listener);
+ removeVcnNetworkPolicyChangeListener(listener);
}
/**
@@ -233,20 +234,20 @@ public class VcnManager {
}
/**
- * VcnNetworkPolicyListener is the interface through which internal system components (e.g.
- * Network Factories) can register to receive updates for VCN-underlying Network policies from
- * the System Server.
+ * VcnNetworkPolicyChangeListener is the interface through which internal system components
+ * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies
+ * from the System Server.
*
* <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks
- * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify
- * the registrant when VCN Network policies change. Upon receiving this signal, the listener
- * must check {@link VcnManager} for the current Network policy result for each of its Networks
- * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+ * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to
+ * notify the registrant when VCN Network policies change. Upon receiving this signal, the
+ * listener must check {@link VcnManager} for the current Network policy result for each of its
+ * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
*
* @hide
*/
@SystemApi
- public interface VcnNetworkPolicyListener {
+ public interface VcnNetworkPolicyChangeListener {
/**
* Notifies the implementation that the VCN's underlying Network policy has changed.
*
@@ -260,20 +261,21 @@ public class VcnManager {
/**
* Add a listener for VCN-underlying Network policy updates.
*
- * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is
- * registered. No callbacks are guaranteed upon registration.
+ * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it
+ * is registered. No callbacks are guaranteed upon registration.
*
* @param executor the Executor that will be used for invoking all calls to the specified
* Listener
- * @param listener the VcnNetworkPolicyListener to be added
+ * @param listener the VcnNetworkPolicyChangeListener to be added
* @throws SecurityException if the caller does not have permission NETWORK_FACTORY
- * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered
+ * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already
+ * registered
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public void addVcnNetworkPolicyListener(
- @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) {
+ public void addVcnNetworkPolicyChangeListener(
+ @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) {
requireNonNull(executor, "executor must not be null");
requireNonNull(listener, "listener must not be null");
@@ -292,15 +294,18 @@ public class VcnManager {
}
/**
- * Remove the specified VcnNetworkPolicyListener from VcnManager.
+ * Remove the specified VcnNetworkPolicyChangeListener from VcnManager.
*
* <p>If the specified listener is not currently registered, this is a no-op.
*
- * @param listener the VcnNetworkPolicyListener that will be removed
+ * @param listener the VcnNetworkPolicyChangeListener that will be removed
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
* @hide
*/
@SystemApi
- public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) {
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public void removeVcnNetworkPolicyChangeListener(
+ @NonNull VcnNetworkPolicyChangeListener listener) {
requireNonNull(listener, "listener must not be null");
VcnUnderlyingNetworkPolicyListenerBinder binder =
@@ -320,8 +325,9 @@ public class VcnManager {
* Applies the network policy for a {@link android.net.Network} with the given parameters.
*
* <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
- * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider
- * MUST poll for the updated Network policy based on that Network's capabilities and properties.
+ * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network
+ * Provider MUST poll for the updated Network policy based on that Network's capabilities and
+ * properties.
*
* @param networkCapabilities the NetworkCapabilities to be used in determining the Network
* policy result for this Network.
@@ -532,17 +538,18 @@ public class VcnManager {
}
/**
- * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server.
+ * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System
+ * Server.
*
* @hide
*/
private static class VcnUnderlyingNetworkPolicyListenerBinder
extends IVcnUnderlyingNetworkPolicyListener.Stub {
@NonNull private final Executor mExecutor;
- @NonNull private final VcnNetworkPolicyListener mListener;
+ @NonNull private final VcnNetworkPolicyChangeListener mListener;
private VcnUnderlyingNetworkPolicyListenerBinder(
- Executor executor, VcnNetworkPolicyListener listener) {
+ Executor executor, VcnNetworkPolicyChangeListener listener) {
mExecutor = executor;
mListener = listener;
}
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
index a86237dd271f..97ec5940ccb0 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -83,4 +83,29 @@ public abstract class BatteryManagerInternal {
* wait on the battery service lock.
*/
public abstract int getInvalidCharger();
+
+ /**
+ * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+ */
+ public abstract void setChargerAcOnline(boolean online, boolean forceUpdate);
+
+ /**
+ * Sets battery level, and freezes the battery state.
+ */
+ public abstract void setBatteryLevel(int level, boolean forceUpdate);
+
+ /**
+ * Unplugs battery, and freezes the battery state.
+ */
+ public abstract void unplugBattery(boolean forceUpdate);
+
+ /**
+ * Unfreezes battery state, returning to current hardware values.
+ */
+ public abstract void resetBattery(boolean forceUpdate);
+
+ /**
+ * Suspend charging even if plugged in.
+ */
+ public abstract void suspendBatteryInput();
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66f7bd9d8dee..fa6472ee4a79 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -986,6 +986,24 @@ public abstract class BatteryStats implements Parcelable {
public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which);
/**
+ * Returns the battery consumption (in microcoulombs) of bluetooth for this uid,
+ * derived from on device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getBluetoothMeasuredBatteryConsumptionUC();
+
+ /**
+ * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
+ * on device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
* derived from on device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
@@ -995,6 +1013,15 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getScreenOnMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of wifi for this uid,
+ * derived from on device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getWifiMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) used by this uid for each
* {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
* type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
@@ -2496,11 +2523,29 @@ public abstract class BatteryStats implements Parcelable {
};
/**
- * Returned value if power data is unavailable
+ * Returned value if power data is unavailable.
*
* {@hide}
*/
- public static final long POWER_DATA_UNAVAILABLE = -1;
+ public static final long POWER_DATA_UNAVAILABLE = -1L;
+
+ /**
+ * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on
+ * device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getBluetoothMeasuredBatteryConsumptionUC();
+
+ /**
+ * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
+ * measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getCpuMeasuredBatteryConsumptionUC();
/**
* Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
@@ -2521,6 +2566,15 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getScreenDozeMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of wifi, derived from on
+ * device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getWifiMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) that each
* {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
* type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}) consumed.
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 1905d708d6d3..e47478abf439 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.net.NetworkStack;
import android.os.connectivity.CellularBatteryStats;
@@ -487,4 +488,74 @@ public final class BatteryStatsManager {
return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
: DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
}
-}
+
+ /**
+ * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ try {
+ mBatteryStats.setChargerAcOnline(online, forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets battery level, and freezes the battery state.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void setBatteryLevel(int level, boolean forceUpdate) {
+ try {
+ mBatteryStats.setBatteryLevel(level, forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unplugs battery, and freezes the battery state.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void unplugBattery(boolean forceUpdate) {
+ try {
+ mBatteryStats.unplugBattery(forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unfreezes battery state, returning to current hardware values.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void resetBattery(boolean forceUpdate) {
+ try {
+ mBatteryStats.resetBattery(forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Suspend charging even if plugged in.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void suspendBatteryInput() {
+ try {
+ mBatteryStats.suspendBatteryInput();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 83f78a56487a..a2edc93c6e5e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -27,6 +27,7 @@ import android.app.ActivityThread;
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.sysprop.DeviceProperties;
import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
import android.text.TextUtils;
@@ -139,7 +140,7 @@ public class Build {
*/
@UnsupportedAppUsage
@TestApi
- public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
+ public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1");
/**
* A hardware serial number, if available. Alphanumeric only, case-insensitive.
@@ -298,6 +299,19 @@ public class Build {
"ro.build.version.security_patch", "");
/**
+ * The media performance class of the device or 0 if none.
+ * <p>
+ * If this value is not <code>0</code>, the device conforms to the media performance class
+ * definition of the SDK version of this value. This value never changes while a device is
+ * booted, but it may increase when the hardware manufacturer provides an OTA update.
+ * <p>
+ * Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
+ * {@link Build.VERSION_CODES#S}.
+ */
+ public static final int MEDIA_PERFORMANCE_CLASS =
+ DeviceProperties.media_performance_class().orElse(0);
+
+ /**
* The user-visible SDK version of the framework in its raw String
* representation; use {@link #SDK_INT} instead.
*
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 124c0b00b2b2..21bf8b8c30e4 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -728,11 +728,11 @@ public class Environment {
/**
* Standard directory in which to place any audio files that should be
* in the regular list of music for the user.
- * This may be combined with
+ * This may be combined with {@link #DIRECTORY_AUDIOBOOKS},
* {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
- * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
- * of directories to categories a particular audio file as more than one
- * type.
+ * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+ * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+ * categorize a particular audio file as more than one type.
*/
public static String DIRECTORY_MUSIC = "Music";
@@ -741,10 +741,10 @@ public class Environment {
* in the list of podcasts that the user can select (not as regular
* music).
* This may be combined with {@link #DIRECTORY_MUSIC},
- * {@link #DIRECTORY_NOTIFICATIONS},
- * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
- * of directories to categories a particular audio file as more than one
- * type.
+ * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_NOTIFICATIONS},
+ * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+ * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+ * categorize a particular audio file as more than one type.
*/
public static String DIRECTORY_PODCASTS = "Podcasts";
@@ -753,10 +753,10 @@ public class Environment {
* in the list of ringtones that the user can select (not as regular
* music).
* This may be combined with {@link #DIRECTORY_MUSIC},
- * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
- * {@link #DIRECTORY_ALARMS} as a series
- * of directories to categories a particular audio file as more than one
- * type.
+ * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+ * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+ * to categorize a particular audio file as more than one type.
*/
public static String DIRECTORY_RINGTONES = "Ringtones";
@@ -765,10 +765,10 @@ public class Environment {
* in the list of alarms that the user can select (not as regular
* music).
* This may be combined with {@link #DIRECTORY_MUSIC},
- * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
- * and {@link #DIRECTORY_RINGTONES} as a series
- * of directories to categories a particular audio file as more than one
- * type.
+ * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_RINGTONES},
+ * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+ * to categorize a particular audio file as more than one type.
*/
public static String DIRECTORY_ALARMS = "Alarms";
@@ -777,10 +777,10 @@ public class Environment {
* in the list of notifications that the user can select (not as regular
* music).
* This may be combined with {@link #DIRECTORY_MUSIC},
- * {@link #DIRECTORY_PODCASTS},
- * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
- * of directories to categories a particular audio file as more than one
- * type.
+ * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+ * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+ * categorize a particular audio file as more than one type.
*/
public static String DIRECTORY_NOTIFICATIONS = "Notifications";
@@ -831,14 +831,26 @@ public class Environment {
public static String DIRECTORY_SCREENSHOTS = "Screenshots";
/**
- * Standard directory in which to place any audio files which are
- * audiobooks.
+ * Standard directory in which to place any audio files that should be
+ * in the list of audiobooks that the user can select (not as regular
+ * music).
+ * This may be combined with {@link #DIRECTORY_MUSIC},
+ * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+ * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES},
+ * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+ * to categorize a particular audio file as more than one type.
*/
public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
/**
- * Standard directory in which to place any audio files which are
- * recordings.
+ * Standard directory in which to place any audio files that should be
+ * in the list of voice recordings recorded by voice recorder apps that
+ * the user can select (not as regular music).
+ * This may be combined with {@link #DIRECTORY_MUSIC},
+ * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+ * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+ * and {@link #DIRECTORY_RINGTONES} as a series of directories
+ * to categorize a particular audio file as more than one type.
*/
@NonNull
// The better way is that expose a static method getRecordingDirectories.
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index a46af9754f32..b12dad038ce3 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1247,9 +1247,9 @@ public final class FileUtils {
}
/**
- * Creates a directory with name {@code name} under an existing directory {@code baseDir}.
- * Returns a {@code File} object representing the directory on success, {@code null} on
- * failure.
+ * Creates a directory with name {@code name} under an existing directory {@code baseDir} if it
+ * doesn't exist already. Returns a {@code File} object representing the directory if it exists
+ * and {@code null} if not.
*
* @hide
*/
@@ -1259,13 +1259,23 @@ public final class FileUtils {
return createDir(dir) ? dir : null;
}
- /** @hide */
+ /**
+ * Ensure the given directory exists, creating it if needed. This method is threadsafe.
+ *
+ * @return false if the directory doesn't exist and couldn't be created
+ *
+ * @hide
+ */
public static boolean createDir(File dir) {
+ if (dir.mkdir()) {
+ return true;
+ }
+
if (dir.exists()) {
return dir.isDirectory();
}
- return dir.mkdir();
+ return false;
}
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 786a7d08047e..2c4d130788cc 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -197,6 +197,9 @@ public final class PowerManager {
* application as the normal behavior. Notifications that pop up and want
* the device to be on are the exception; use this flag to be like them.
* </p><p>
+ * Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any
+ * wake-up, including wake-ups triggered by wake locks.
+ * </p><p>
* Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
* </p>
*/
@@ -323,6 +326,13 @@ public final class PowerManager {
public static final int USER_ACTIVITY_EVENT_ATTENTION = 4;
/**
+ * User activity event type: {@link com.android.server.power.FaceDownDetector} taking action
+ * on behalf of user.
+ * @hide
+ */
+ public static final int USER_ACTIVITY_EVENT_FACE_DOWN = 5;
+
+ /**
* User activity flag: If already dimmed, extend the dim timeout
* but do not brighten. This flag is useful for keeping the screen on
* a little longer without causing a visible change such as when
@@ -1292,6 +1302,9 @@ public final class PowerManager {
* device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
* default display group} is already off then nothing will happen.
*
+ * <p>If the device is an Android TV playback device and the current active source on the
+ * HDMI-connected TV, it will attempt to turn off that TV via HDMI-CEC.
+ *
* <p>
* Overrides all the wake locks that are held.
* This is what happens when the power key is pressed to turn off the screen.
@@ -1423,6 +1436,10 @@ public final class PowerManager {
* be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
* android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
*
+ * <p>If the device is an Android TV playback device, it will attempt to turn on the
+ * HDMI-connected TV and become the current active source via the HDMI-CEC One Touch Play
+ * feature.
+ *
* <p>
* This is what happens when the power key is pressed to turn on the screen.
* </p><p>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e75e224d9a6f..ab1f688d60ca 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,8 +16,11 @@
package android.os;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.system.ErrnoException;
@@ -108,6 +111,7 @@ public class Process {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int VPN_UID = 1016;
/**
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index df4ade09753b..d89c3d591d46 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -21,6 +21,7 @@ import android.util.ArrayMap;
import android.util.Slog;
import java.io.PrintWriter;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -354,6 +355,23 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
+ * Performs {@code action} on each callback and associated cookie, calling {@link
+ * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
+ *
+ * @hide
+ */
+ public <C> void broadcast(BiConsumer<E, C> action) {
+ int itemCount = beginBroadcast();
+ try {
+ for (int i = 0; i < itemCount; i++) {
+ action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+ }
+ } finally {
+ finishBroadcast();
+ }
+ }
+
+ /**
* Returns the number of registered callbacks. Note that the number of registered
* callbacks may differ from the value returned by {@link #beginBroadcast()} since
* the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index b003d238c268..b90d438ffb93 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -208,6 +208,28 @@ public abstract class Vibrator {
public abstract boolean hasAmplitudeControl();
/**
+ * Gets the resonant frequency of the vibrator.
+ *
+ * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ * @hide
+ */
+ public float getResonantFrequency() {
+ return Float.NaN;
+ }
+
+ /**
+ * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+ *
+ * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ * @hide
+ */
+ public float getQFactor() {
+ return Float.NaN;
+ }
+
+ /**
* Configure an always-on haptics effect.
*
* @param alwaysOnId The board-specific always-on ID to configure.
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 50d2de3da965..3121b952281e 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -42,21 +42,27 @@ public final class VibratorInfo implements Parcelable {
private final SparseBooleanArray mSupportedEffects;
@Nullable
private final SparseBooleanArray mSupportedPrimitives;
+ private final float mResonantFrequency;
+ private final float mQFactor;
VibratorInfo(Parcel in) {
mId = in.readInt();
mCapabilities = in.readLong();
mSupportedEffects = in.readSparseBooleanArray();
mSupportedPrimitives = in.readSparseBooleanArray();
+ mResonantFrequency = in.readFloat();
+ mQFactor = in.readFloat();
}
/** @hide */
public VibratorInfo(int id, long capabilities, int[] supportedEffects,
- int[] supportedPrimitives) {
+ int[] supportedPrimitives, float resonantFrequency, float qFactor) {
mId = id;
mCapabilities = capabilities;
mSupportedEffects = toSparseBooleanArray(supportedEffects);
mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
+ mResonantFrequency = resonantFrequency;
+ mQFactor = qFactor;
}
@Override
@@ -65,6 +71,8 @@ public final class VibratorInfo implements Parcelable {
dest.writeLong(mCapabilities);
dest.writeSparseBooleanArray(mSupportedEffects);
dest.writeSparseBooleanArray(mSupportedPrimitives);
+ dest.writeFloat(mResonantFrequency);
+ dest.writeFloat(mQFactor);
}
@Override
@@ -83,12 +91,15 @@ public final class VibratorInfo implements Parcelable {
VibratorInfo that = (VibratorInfo) o;
return mId == that.mId && mCapabilities == that.mCapabilities
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
- && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives);
+ && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
+ && Objects.equals(mResonantFrequency, that.mResonantFrequency)
+ && Objects.equals(mQFactor, that.mQFactor);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives);
+ return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
+ mResonantFrequency, mQFactor);
}
@Override
@@ -99,6 +110,8 @@ public final class VibratorInfo implements Parcelable {
+ ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+ + ", mResonantFrequency=" + mResonantFrequency
+ + ", mQFactor=" + mQFactor
+ '}';
}
@@ -156,6 +169,26 @@ public final class VibratorInfo implements Parcelable {
return (mCapabilities & capability) == capability;
}
+ /**
+ * Gets the resonant frequency of the vibrator.
+ *
+ * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ */
+ public float getResonantFrequency() {
+ return mResonantFrequency;
+ }
+
+ /**
+ * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+ *
+ * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ */
+ public float getQFactor() {
+ return mQFactor;
+ }
+
private String[] getCapabilitiesNames() {
List<String> names = new ArrayList<>();
if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
index 016cc2f3cdad..ad74a9f5c906 100644
--- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -110,7 +110,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
}
// Calculate energy used using PowerProfile.
PowerProfile powerProfile = new PowerProfile(context);
- final double rxIdleCurrent = powerProfile.getAveragePower(
+ final double idleCurrent = powerProfile.getAveragePower(
PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
final double rxCurrent = powerProfile.getAveragePower(
PowerProfile.POWER_WIFI_CONTROLLER_RX);
@@ -121,7 +121,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
return (long) ((txDurationMillis * txCurrent
+ rxDurationMillis * rxCurrent
- + idleDurationMillis * rxIdleCurrent)
+ + idleDurationMillis * idleCurrent)
* voltage);
}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 73520e07d118..2a42b981ac26 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -51,6 +51,8 @@ import java.util.UUID;
public final class IncrementalFileStorages {
private static final String TAG = "IncrementalFileStorages";
+ private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
+
private @NonNull final IncrementalManager mIncrementalManager;
private @NonNull final File mStageDir;
private @Nullable IncrementalStorage mInheritedStorage;
@@ -116,7 +118,10 @@ public final class IncrementalFileStorages {
mInheritedStorage = mIncrementalManager.openStorage(
inheritedDir.getAbsolutePath());
if (mInheritedStorage != null) {
- if (!mInheritedStorage.isFullyLoaded()) {
+ boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+ dataLoaderParams.getComponentName().getPackageName());
+ if (systemDataLoader && !mInheritedStorage.isFullyLoaded()) {
+ // System data loader does not support incomplete storages.
throw new IOException("Inherited storage has missing pages.");
}
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index dc6f63a94685..047c05a8734b 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -30,6 +30,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -265,6 +266,13 @@ public final class IncrementalManager {
}
/**
+ * Checks if an fd corresponds to a file on a mounted Incremental File System.
+ */
+ public static boolean isIncrementalFileFd(@NonNull FileDescriptor fd) {
+ return nativeIsIncrementalFd(fd.getInt$());
+ }
+
+ /**
* Returns raw signature for file if it's on Incremental File System.
* Unsafe, use only if you are sure what you are doing.
*/
@@ -421,9 +429,22 @@ public final class IncrementalManager {
storage.unregisterStorageHealthListener();
}
+ /**
+ * Returns the metrics of an Incremental Storage.
+ */
+ public IncrementalMetrics getMetrics(@NonNull String codePath) {
+ final IncrementalStorage storage = openStorage(codePath);
+ if (storage == null) {
+ // storage does not exist, package not installed
+ return null;
+ }
+ return new IncrementalMetrics(storage.getMetrics());
+ }
+
/* Native methods */
private static native boolean nativeIsEnabled();
private static native boolean nativeIsV2Available();
private static native boolean nativeIsIncrementalPath(@NonNull String path);
+ private static native boolean nativeIsIncrementalFd(@NonNull int fd);
private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
}
diff --git a/core/java/android/os/incremental/IncrementalMetrics.java b/core/java/android/os/incremental/IncrementalMetrics.java
new file mode 100644
index 000000000000..44dea1be50f0
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalMetrics.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+/**
+ * Provides methods to access metrics about an app installed via Incremental
+ * @hide
+ */
+public class IncrementalMetrics {
+ @NonNull private final PersistableBundle mData;
+
+ public IncrementalMetrics(@NonNull PersistableBundle data) {
+ mData = data;
+ }
+
+ /**
+ * @return Milliseconds between now and when the oldest pending read happened
+ */
+ public long getMillisSinceOldestPendingRead() {
+ return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_OLDEST_PENDING_READ, -1);
+ }
+}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index e6ce8cd56d28..7cf0144d71f7 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import java.io.File;
@@ -601,4 +602,17 @@ public final class IncrementalStorage {
return;
}
}
+
+ /**
+ * Returns the metrics of the current storage.
+ * {@see IIncrementalService} for metrics keys.
+ */
+ public PersistableBundle getMetrics() {
+ try {
+ return mService.getMetrics(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 396ba2d3cea5..82c4c715f4b0 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -52,6 +52,11 @@ public abstract class StorageManagerInternal {
}
/**
+ * Return true if fuse is mounted.
+ */
+ public abstract boolean isFuseMounted(int userId);
+
+ /**
* Create storage directories if it does not exist.
* Return true if the directories were setup correctly, otherwise false.
*/
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index bae36b299247..177e422e7851 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -41,6 +41,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.location.LocationManager;
import android.media.AudioManager;
import android.os.Build;
import android.os.Handler;
@@ -50,12 +51,14 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.Immutable;
import com.android.internal.util.CollectionUtils;
@@ -82,6 +85,8 @@ public final class PermissionManager {
public static final String KILL_APP_REASON_GIDS_CHANGED =
"permission grant or revoke changed gids";
+ private static final String SYSTEM_PKG = "android";
+
/**
* Refuse to install package if groups of permissions are bad
* - Permission groups should only be shared between apps sharing a certificate
@@ -857,6 +862,23 @@ public final class PermissionManager {
}
/**
+ * Check if this package/op combination is exempted from indicators
+ * @return
+ * @hide
+ */
+ public static boolean isSpecialCaseShownIndicator(@NonNull Context context,
+ @NonNull String packageName) {
+
+ if (packageName.equals(SYSTEM_PKG)) {
+ return false;
+ }
+
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
+ false)
+ || packageName.equals(context.getString(R.string.config_systemSpeechRecognizer))
+ || context.getSystemService(LocationManager.class).isProviderPackage(packageName);
+ }
+ /**
* Gets the list of packages that have permissions that specified
* {@code requestDontAutoRevokePermissions=true} in their
* {@code application} manifest declaration.
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 4c9e77c35135..80a3e1693ab1 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -31,40 +31,25 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_W
import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
-import android.Manifest;
import android.annotation.NonNull;
import android.app.AppOpsManager;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.Attribution;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.icu.text.ListFormatter;
-import android.location.LocationManager;
import android.media.AudioManager;
import android.os.Process;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.speech.RecognitionService;
-import android.speech.RecognizerIntent;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.internal.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
/**
* A helper which gets all apps which have used microphone, camera, and possible location
@@ -111,8 +96,7 @@ public class PermissionUsageHelper {
private static boolean shouldShowLocationIndicator() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_INDICATORS_ENABLED, false)
- || shouldShowPermissionsHub();
+ PROPERTY_LOCATION_INDICATORS_ENABLED, false);
}
private static long getRecentThreshold(Long now) {
@@ -166,7 +150,7 @@ public class PermissionUsageHelper {
* Constructor for PermissionUsageHelper
* @param context The context from which to derive the package information
*/
- public PermissionUsageHelper(Context context) {
+ public PermissionUsageHelper(@NonNull Context context) {
mContext = context;
mPkgManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
@@ -181,26 +165,10 @@ public class PermissionUsageHelper {
return mUserContexts.get(user);
}
- // TODO ntmyren: Replace this with better check if this moves beyond teamfood
- private boolean isAppPredictor(String packageName, UserHandle user) {
- return shouldShowPermissionsHub() && getUserContext(user).getPackageManager()
- .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private boolean isSpeechRecognizerUsage(String op, String packageName) {
- if (!OPSTR_RECORD_AUDIO.equals(op)) {
- return false;
- }
-
- return packageName.equals(
- mContext.getString(R.string.config_systemSpeechRecognizer));
- }
-
/**
* @see PermissionManager.getIndicatorAppOpUsageData
*/
- public List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
+ public @NonNull List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
List<PermGroupUsage> usages = new ArrayList<>();
if (!shouldShowIndicators()) {
@@ -216,9 +184,6 @@ public class PermissionUsageHelper {
}
Map<String, List<OpUsage>> rawUsages = getOpUsages(ops);
- Set<List<PackageAttribution>> proxyChains = getProxyChains(rawUsages.get(MICROPHONE));
- Map<PackageAttribution, CharSequence> packagesWithAttributionLabels =
- getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains);
ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
@@ -246,15 +211,8 @@ public class PermissionUsageHelper {
boolean isPhone = false;
String permGroup = usedPermGroups.get(permGroupNum);
- Map<PackageAttribution, CharSequence> pkgAttrLabels = packagesWithAttributionLabels;
- Set<List<PackageAttribution>> proxies = proxyChains;
- if (!MICROPHONE.equals(permGroup)) {
- pkgAttrLabels = new ArrayMap<>();
- proxies = new ArraySet<>();
- }
-
- List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup),
- pkgAttrLabels.keySet(), proxies);
+ ArrayMap<OpUsage, CharSequence> usagesWithLabels =
+ getUniqueUsagesWithLabels(rawUsages.get(permGroup));
if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
isPhone = true;
@@ -264,11 +222,11 @@ public class PermissionUsageHelper {
permGroup = CAMERA;
}
- for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) {
- OpUsage usage = permUsages.get(usageNum);
+ for (int usageNum = 0; usageNum < usagesWithLabels.size(); usageNum++) {
+ OpUsage usage = usagesWithLabels.keyAt(usageNum);
usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup,
usage.lastAccessTime, usage.isRunning, isPhone,
- packagesWithAttributionLabels.get(usage.toPackageAttr())));
+ usagesWithLabels.valueAt(usageNum)));
}
}
@@ -298,7 +256,7 @@ public class PermissionUsageHelper {
long recentThreshold = getRecentThreshold(now);
long runningThreshold = getRunningThreshold(now);
int opFlags = OP_FLAGS_ALL_TRUSTED;
- Map<String, Map<PackageAttribution, OpUsage>> usages = new ArrayMap<>();
+ Map<String, Map<Integer, OpUsage>> usages = new ArrayMap<>();
int numPkgOps = ops.size();
for (int pkgOpNum = 0; pkgOpNum < numPkgOps; pkgOpNum++) {
@@ -326,10 +284,8 @@ public class PermissionUsageHelper {
}
if (packageName.equals(SYSTEM_PKG)
- || (!isUserSensitive(packageName, user, op)
- && !isLocationProvider(packageName, user)
- && !isAppPredictor(packageName, user))
- && !isSpeechRecognizerUsage(op, packageName)) {
+ || (!shouldShowPermissionsHub()
+ && !isUserSensitive(packageName, user, op))) {
continue;
}
@@ -340,20 +296,20 @@ public class PermissionUsageHelper {
AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags);
if (proxy != null && proxy.getPackageName() != null) {
proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(),
- proxy.getUid(), lastAccessTime, isRunning, null);
+ op, proxy.getUid(), lastAccessTime, isRunning, null);
}
String permGroupName = getGroupForOp(op);
- OpUsage usage = new OpUsage(packageName, attributionTag, uid,
+ OpUsage usage = new OpUsage(packageName, attributionTag, op, uid,
lastAccessTime, isRunning, proxyUsage);
- PackageAttribution packageAttr = usage.toPackageAttr();
+ Integer packageAttr = usage.getPackageAttrHash();
if (!usages.containsKey(permGroupName)) {
- ArrayMap<PackageAttribution, OpUsage> map = new ArrayMap<>();
+ ArrayMap<Integer, OpUsage> map = new ArrayMap<>();
map.put(packageAttr, usage);
usages.put(permGroupName, map);
} else {
- Map<PackageAttribution, OpUsage> permGroupUsages =
+ Map<Integer, OpUsage> permGroupUsages =
usages.get(permGroupName);
if (!permGroupUsages.containsKey(packageAttr)) {
permGroupUsages.put(packageAttr, usage);
@@ -375,380 +331,119 @@ public class PermissionUsageHelper {
return flattenedUsages;
}
- /**
- * Take the list of all usages, figure out any proxy chains, get all possible special
- * attribution labels, and figure out which usages need to show a special label, if any.
- *
- * @param usages The raw permission usages
- *
- * @return A map of package + attribution (in the form of a PackageAttribution object) to
- * trusted attribution label, if there is one
- */
- private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions(
- List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) {
- ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>();
- if (usages == null) {
- return attributions;
- }
-
- Map<PackageAttribution, CharSequence> trustedLabels =
- getTrustedAttributionLabels(usages);
-
- for (List<PackageAttribution> chain : proxyChains) {
- // If this chain is empty, or has only one link, then do not show any special labels
- if (chain.size() <= 1) {
- continue;
- }
-
- // If the last link in the chain is not user sensitive, do not show it.
- boolean lastLinkIsUserSensitive = false;
- for (int i = 0; i < usages.size(); i++) {
- PackageAttribution lastLink = chain.get(chain.size() - 1);
- if (lastLink.equals(usages.get(i).toPackageAttr())) {
- lastLinkIsUserSensitive = true;
- break;
- }
- }
- if (!lastLinkIsUserSensitive) {
- continue;
- }
-
- List<CharSequence> labels = new ArrayList<>();
- for (int i = 0; i < chain.size(); i++) {
- // If this is the last link in the proxy chain, assign it the series of labels
- // Else, if it has a special label, add that label
- // Else, if there are no other apps in the remaining part of the chain which
- // have the same package name, add the app label
- // If it is not the last link in the chain, remove its attribution
- PackageAttribution attr = chain.get(i);
- CharSequence trustedLabel = trustedLabels.get(attr);
- if (i == chain.size() - 1) {
- attributions.put(attr, formatLabelList(labels));
- } else if (trustedLabel != null && !labels.contains(trustedLabel)) {
- labels.add(trustedLabel);
- trustedLabels.remove(attr);
- } else {
- boolean remainingChainHasPackage = false;
- for (int attrNum = i + 1; attrNum < chain.size() - 1; attrNum++) {
- if (chain.get(i).packageName.equals(attr.packageName)) {
- remainingChainHasPackage = true;
- break;
- }
- }
- if (!remainingChainHasPackage) {
- try {
- ApplicationInfo appInfo = mPkgManager.getApplicationInfoAsUser(
- attr.packageName, 0, attr.getUser());
- CharSequence appLabel = appInfo.loadLabel(
- getUserContext(attr.getUser()).getPackageManager());
- labels.add(appLabel);
- } catch (PackageManager.NameNotFoundException e) {
- // Do nothing
- }
- }
- }
- }
- }
-
- for (PackageAttribution attr : trustedLabels.keySet()) {
- attributions.put(attr, trustedLabels.get(attr));
- }
-
- return attributions;
- }
-
private CharSequence formatLabelList(List<CharSequence> labels) {
return ListFormatter.getInstance().format(labels);
}
- /**
- * Get all chains of proxy usages. A proxy chain is defined as one usage at the root, then
- * further proxy usages, where the app and attribution tag of the proxy in the proxy usage
- * matches the previous usage in the chain.
- *
- * @param usages The permission usages
- *
- * @return A set of lists of package attributions. One list represents a chain of proxy usages,
- * with the start of the chain (the usage without a proxy) at position 0, and each usage down
- * the chain has the previous one listed as a proxy usage.
- */
- private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) {
- if (usages == null) {
- return new ArraySet<>();
- }
+ private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
+ ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
- ArrayMap<PackageAttribution, ArrayList<PackageAttribution>> proxyChains = new ArrayMap<>();
- // map of usages that still need to be removed, or added to a chain
- ArrayMap<PackageAttribution, OpUsage> remainingUsages = new ArrayMap<>();
- // map of usage.proxy -> usage, telling us if a usage is a proxy
- ArrayMap<PackageAttribution, PackageAttribution> proxies = new ArrayMap<>();
+ if (usages == null) {
+ return usagesAndLabels;
+ }
+
+ ArrayMap<Integer, OpUsage> allUsages = new ArrayMap<>();
+ // map of uid -> most recent non-proxy-related usage for that uid.
+ ArrayMap<Integer, OpUsage> mostRecentUsages = new ArrayMap<>();
+ // set of all uids involved in a proxy usage
+ ArraySet<Integer> proxyUids = new ArraySet<>();
+ // map of usage -> list of proxy app labels
+ ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
+ // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
+ ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
for (int i = 0; i < usages.size(); i++) {
OpUsage usage = usages.get(i);
- remainingUsages.put(usage.toPackageAttr(), usage);
+ allUsages.put(usage.getPackageAttrHash(), usage);
if (usage.proxy != null) {
- proxies.put(usage.proxy.toPackageAttr(), usage.toPackageAttr());
+ proxies.put(usage.proxy.getPackageAttrHash(), usage);
}
}
- // find all possible end points for chains
- List<PackageAttribution> keys = new ArrayList<>(remainingUsages.keySet());
- for (int usageNum = 0; usageNum < remainingUsages.size(); usageNum++) {
- OpUsage usage = remainingUsages.get(keys.get(usageNum));
+ // find all possible end points for chains, and find the most recent of the rest of the uses
+ for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
+ OpUsage usage = usages.get(usageNum);
if (usage == null) {
continue;
}
- PackageAttribution usageAttr = usage.toPackageAttr();
+
+ int usageAttr = usage.getPackageAttrHash();
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
- // If it has no proxy, and isn't a proxy, remove it.
if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
- ArrayList<PackageAttribution> proxyList = new ArrayList<>();
- proxyList.add(usageAttr);
- proxyChains.put(usageAttr, proxyList);
- } else if (!proxies.containsKey(usageAttr) && usage.proxy == null) {
- remainingUsages.remove(keys.get(usageNum));
+ proxyLabels.put(usage, new ArrayList<>());
+ proxyUids.add(usage.uid);
+ }
+ if (!mostRecentUsages.containsKey(usage.uid) || usage.lastAccessTime
+ > mostRecentUsages.get(usage.uid).lastAccessTime) {
+ mostRecentUsages.put(usage.uid, usage);
}
}
- // assemble the chains in reverse order, then invert them
- for (int numStart = 0; numStart < proxyChains.size(); numStart++) {
- PackageAttribution currPackageAttr = proxyChains.keyAt(numStart);
- ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr);
- OpUsage currentUsage = remainingUsages.get(currPackageAttr);
- if (currentUsage == null || proxyChain == null) {
+ // get all the proxy labels
+ for (int numStart = 0; numStart < proxyLabels.size(); numStart++) {
+ OpUsage start = proxyLabels.keyAt(numStart);
+ // Remove any non-proxy usage for the starting uid
+ mostRecentUsages.remove(start.uid);
+ OpUsage currentUsage = proxyLabels.keyAt(numStart);
+ ArrayList<CharSequence> proxyLabelList = proxyLabels.get(currentUsage);
+ if (currentUsage == null || proxyLabelList == null) {
continue;
}
+ int iterNum = 0;
+ int maxUsages = allUsages.size();
while (currentUsage.proxy != null) {
- currPackageAttr = currentUsage.proxy.toPackageAttr();
- currentUsage = remainingUsages.get(currPackageAttr);
-
- boolean invalidState = false;
- for (int chainNum = 0; chainNum < proxyChain.size(); chainNum++) {
- if (currentUsage == null || proxyChain.get(chainNum).equals(currPackageAttr)) {
- // either our current value is not in the usage list, or we have a cycle
- invalidState = true;
- break;
- }
- }
-
- if (invalidState) {
- break;
- }
-
- proxyChain.add(currPackageAttr);
- }
- // invert the lists, so the element without a proxy is first on the list
- Collections.reverse(proxyChain);
- }
- return new ArraySet<>(proxyChains.values());
- }
-
- /**
- * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the
- * label needed to display with it, as well as information about the proxy whose label is being
- * shown, if applicable.
- *
- * @param usages The permission usages
- *
- * @return A map of package attribution -> the attribution label for that package attribution,
- * if applicable
- */
- private Map<PackageAttribution, CharSequence> getTrustedAttributionLabels(
- List<OpUsage> usages) {
- List<UserHandle> users = new ArrayList<>();
- for (int i = 0; i < usages.size(); i++) {
- UserHandle user = UserHandle.getUserHandleForUid(usages.get(i).uid);
- if (!users.contains(user)) {
- users.add(user);
- }
- }
-
- Map<PackageAttribution, CharSequence> trustedLabels = new ArrayMap<>();
- for (int userNum = 0; userNum < users.size(); userNum++) {
- UserHandle user = users.get(userNum);
- Context userContext = mContext.createContextAsUser(user, 0);
-
- // Get all voice IME labels
- Map<String, CharSequence> voiceInputs = new ArrayMap<>();
- List<InputMethodInfo> inputs = userContext.getSystemService(InputMethodManager.class)
- .getEnabledInputMethodList();
- for (int inputNum = 0; inputNum < inputs.size(); inputNum++) {
- InputMethodInfo input = inputs.get(inputNum);
- for (int subtypeNum = 0; subtypeNum < input.getSubtypeCount(); subtypeNum++) {
- if (VOICE_IME_SUBTYPE.equals(input.getSubtypeAt(subtypeNum).getMode())) {
- voiceInputs.put(input.getPackageName(), input.getServiceInfo()
- .loadUnsafeLabel(userContext.getPackageManager()));
+ if (allUsages.containsKey(currentUsage.proxy.getPackageAttrHash())) {
+ currentUsage = allUsages.get(currentUsage.proxy.getPackageAttrHash());
+ } else {
+ // We are missing the proxy usage. This may be because it's a one-step trusted
+ // proxy. Check if we should show the proxy label, and show it, if so.
+ OpUsage proxy = currentUsage.proxy;
+ if (PermissionManager.isSpecialCaseShownIndicator(mContext, proxy.packageName)
+ || isUserSensitive(proxy.packageName, proxy.getUser(), proxy.op)) {
+ currentUsage = proxy;
+ // We've effectively added one usage, so increment the max number of usages
+ maxUsages++;
+ } else {
break;
}
}
- }
- // Get the currently selected recognizer from the secure setting
- String recognitionPackageName = Settings.Secure.getString(
- userContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
- if (recognitionPackageName == null) {
- continue;
- }
- recognitionPackageName =
- ComponentName.unflattenFromString(recognitionPackageName).getPackageName();
- Map<String, CharSequence> recognizers = new ArrayMap<>();
- List<ResolveInfo> availableRecognizers = mPkgManager.queryIntentServicesAsUser(
- new Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
- user.getIdentifier());
- for (int recogNum = 0; recogNum < availableRecognizers.size(); recogNum++) {
- ResolveInfo info = availableRecognizers.get(recogNum);
- if (recognitionPackageName.equals(info.serviceInfo.packageName)) {
- recognizers.put(recognitionPackageName, info.serviceInfo.loadUnsafeLabel(
- userContext.getPackageManager()));
- }
- }
- Map<String, CharSequence> recognizerIntents = new ArrayMap<>();
- List<ResolveInfo> availableRecognizerIntents = mPkgManager.queryIntentActivitiesAsUser(
- new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
- PackageManager.GET_META_DATA, user);
- for (int recogNum = 0; recogNum < availableRecognizerIntents.size(); recogNum++) {
- ResolveInfo info = availableRecognizerIntents.get(recogNum);
- if (info.activityInfo == null) {
- continue;
- }
- String pkgName = info.activityInfo.packageName;
- if (recognitionPackageName.equals(pkgName) && recognizers.containsKey(pkgName)) {
- recognizerIntents.put(pkgName, recognizers.get(pkgName));
- }
- }
- for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
- setTrustedAttrsForAccess(usages.get(usageNum), user, false, voiceInputs,
- trustedLabels);
- setTrustedAttrsForAccess(usages.get(usageNum), user, false, recognizerIntents,
- trustedLabels);
- setTrustedAttrsForAccess(usages.get(usageNum), user, true, recognizers,
- trustedLabels);
- }
- }
-
- return trustedLabels;
- }
-
- private void setTrustedAttrsForAccess(OpUsage opUsage, UserHandle currUser, boolean getProxy,
- Map<String, CharSequence> trustedMap, Map<PackageAttribution, CharSequence> toSetMap) {
- OpUsage usage = opUsage;
- if (getProxy) {
- usage = opUsage.proxy;
- }
-
- if (usage == null || !usage.getUser().equals(currUser)
- || !trustedMap.containsKey(usage.packageName)) {
- return;
- }
-
- CharSequence label = getAttributionLabel(usage);
- if (trustedMap.get(usage.packageName).equals(label)) {
- toSetMap.put(opUsage.toPackageAttr(), label);
- }
- }
-
- private CharSequence getAttributionLabel(OpUsage usage) {
- if (usage.attributionTag == null) {
- return null;
- }
-
- PackageInfo pkgInfo;
- try {
- pkgInfo = mPkgManager.getPackageInfoAsUser(usage.packageName,
- PackageManager.GET_ATTRIBUTIONS, usage.getUser().getIdentifier());
- if (pkgInfo.attributions == null || pkgInfo.attributions.length == 0) {
- return null;
- }
- for (int attrNum = 0; attrNum < pkgInfo.attributions.length; attrNum++) {
- Attribution attr = pkgInfo.attributions[attrNum];
- if (usage.attributionTag.equals(attr.getTag())) {
- return mContext.createPackageContextAsUser(usage.packageName, 0,
- usage.getUser()).getString(attr.getLabel());
- }
- }
- return null;
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- }
-
- /**
- * If we have multiple usages of a
- * @param rawUsages The list of all usages that we wish to
- * @param specialAttributions A set of all usages that have a special label
- * @param proxies A list of proxy chains- all links but the last on the chain should be removed,
- * if the last link has a special label
- * @return A list of usages without duplicates or proxy usages.
- */
- private List<OpUsage> removeDuplicatesAndProxies(List<OpUsage> rawUsages,
- Set<PackageAttribution> specialAttributions,
- Set<List<PackageAttribution>> proxies) {
- List<OpUsage> deDuped = new ArrayList<>();
- if (rawUsages == null) {
- return deDuped;
- }
-
- List<PackageAttribution> toRemoveProxies = new ArrayList<>();
- for (List<PackageAttribution> proxyList: proxies) {
- PackageAttribution lastLink = proxyList.get(proxyList.size() - 1);
- if (!specialAttributions.contains(lastLink)) {
- continue;
- }
- for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) {
- if (!proxyList.get(proxyNum).equals(lastLink)) {
- toRemoveProxies.add(proxyList.get(proxyNum));
- }
- }
- }
-
- for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) {
- OpUsage usage = rawUsages.get(usageNum);
-
- // If this attribution is a proxy, remove it
- if (toRemoveProxies.contains(usage.toPackageAttr())) {
- continue;
- }
-
- // If this attribution has a special attribution, do not remove it
- if (specialAttributions.contains(usage.toPackageAttr())) {
- deDuped.add(usage);
- continue;
- }
-
-
- // Search the rest of the list for usages with the same UID. If this is the most recent
- // usage for that uid, keep it. Otherwise, remove it
- boolean isMostRecentForUid = true;
- for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) {
- // Do not compare this usage to itself
- if (otherUsageNum == usageNum) {
- continue;
+ if (currentUsage == null || iterNum == maxUsages
+ || currentUsage.getPackageAttrHash() == start.getPackageAttrHash()) {
+ // We have an invalid state, or a cycle, so break
+ break;
}
- OpUsage otherUsage = rawUsages.get(otherUsageNum);
- if (otherUsage.uid == usage.uid) {
- if (otherUsage.isRunning && !usage.isRunning) {
- isMostRecentForUid = false;
- } else if (usage.isRunning
- && otherUsage.lastAccessTime >= usage.lastAccessTime) {
- isMostRecentForUid = false;
- } else if (otherUsage.lastAccessTime >= usage.lastAccessTime) {
- isMostRecentForUid = false;
- }
-
- if (!isMostRecentForUid) {
- break;
+ proxyUids.add(currentUsage.uid);
+ try {
+ PackageManager userPkgManager =
+ getUserContext(currentUsage.getUser()).getPackageManager();
+ ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
+ currentUsage.packageName, 0);
+ CharSequence appLabel = appInfo.loadLabel(userPkgManager);
+ // If we don't already have the app label, and it's not the same as the main
+ // app, add it
+ if (!proxyLabelList.contains(appLabel)
+ && !currentUsage.packageName.equals(start.packageName)) {
+ proxyLabelList.add(appLabel);
}
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore
}
+ iterNum++;
}
+ usagesAndLabels.put(start,
+ proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
+ }
- if (isMostRecentForUid) {
- deDuped.add(usage);
+ for (int uid : mostRecentUsages.keySet()) {
+ if (!proxyUids.contains(uid)) {
+ usagesAndLabels.put(mostRecentUsages.get(uid), null);
}
}
- return deDuped;
+ return usagesAndLabels;
}
private boolean isUserSensitive(String packageName, UserHandle user, String op) {
@@ -764,11 +459,6 @@ public class PermissionUsageHelper {
return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
}
- private boolean isLocationProvider(String packageName, UserHandle user) {
- return getUserContext(user)
- .getSystemService(LocationManager.class).isProviderPackage(packageName);
- }
-
/**
* Represents the usage of an App op by a particular package and attribution
*/
@@ -776,62 +466,45 @@ public class PermissionUsageHelper {
public final String packageName;
public final String attributionTag;
+ public final String op;
public final int uid;
public final long lastAccessTime;
public final OpUsage proxy;
public final boolean isRunning;
- OpUsage(String packageName, String attributionTag, int uid, long lastAccessTime,
+ OpUsage(String packageName, String attributionTag, String op, int uid, long lastAccessTime,
boolean isRunning, OpUsage proxy) {
- this.isRunning = isRunning;
this.packageName = packageName;
this.attributionTag = attributionTag;
+ this.op = op;
this.uid = uid;
this.lastAccessTime = lastAccessTime;
+ this.isRunning = isRunning;
this.proxy = proxy;
}
- public PackageAttribution toPackageAttr() {
- return new PackageAttribution(packageName, attributionTag, uid);
- }
-
public UserHandle getUser() {
return UserHandle.getUserHandleForUid(uid);
}
- }
- /**
- * A unique identifier for one package attribution, made up of attribution tag, package name
- * and user
- */
- private static class PackageAttribution {
- public final String packageName;
- public final String attributionTag;
- public final int uid;
+ public int getPackageAttrHash() {
+ return Objects.hash(packageName, attributionTag, uid);
+ }
- PackageAttribution(String packageName, String attributionTag, int uid) {
- this.packageName = packageName;
- this.attributionTag = attributionTag;
- this.uid = uid;
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageName, attributionTag, op, uid, lastAccessTime, isRunning);
}
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof PackageAttribution)) {
+ if (!(obj instanceof OpUsage)) {
return false;
}
- PackageAttribution other = (PackageAttribution) obj;
+ OpUsage other = (OpUsage) obj;
return Objects.equals(packageName, other.packageName) && Objects.equals(attributionTag,
- other.attributionTag) && Objects.equals(uid, other.uid);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(packageName, attributionTag, uid);
- }
-
- public UserHandle getUser() {
- return UserHandle.getUserHandleForUid(uid);
+ other.attributionTag) && Objects.equals(op, other.op) && uid == other.uid
+ && lastAccessTime == other.lastAccessTime && isRunning == other.isRunning;
}
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4dfbb6fa2d05..f0b22a923e0f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4126,7 +4126,6 @@ public final class Settings {
* unset or a match is not made, only the standard color modes will be restored.
* @hide
*/
- @Readable
public static final String DISPLAY_COLOR_MODE_VENDOR_HINT =
"display_color_mode_vendor_hint";
@@ -6163,7 +6162,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String CAMERA_AUTOROTATE = "camera_autorotate";
/**
@@ -6724,7 +6722,6 @@ public final class Settings {
* android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update.
* @hide
*/
- @Readable
public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED =
"location_time_zone_detection_enabled";
@@ -7040,6 +7037,15 @@ public final class Settings {
"enabled_accessibility_services";
/**
+ * List of the notified non-accessibility category accessibility services.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES =
+ "notified_non_accessibility_category_services";
+
+ /**
* List of the accessibility services to which the user has granted
* permission to put the device into touch exploration mode.
*
@@ -7515,7 +7521,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String REDUCE_BRIGHT_COLORS_ACTIVATED =
"reduce_bright_colors_activated";
@@ -7525,7 +7530,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String REDUCE_BRIGHT_COLORS_LEVEL =
"reduce_bright_colors_level";
@@ -7534,7 +7538,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS =
"reduce_bright_colors_persist_across_reboots";
@@ -8245,7 +8248,6 @@ public final class Settings {
* @see #MATCH_CONTENT_FRAMERATE_ALWAYS
* @hide
*/
- @Readable
public static final String MATCH_CONTENT_FRAME_RATE =
"match_content_frame_rate";
@@ -8369,6 +8371,13 @@ public final class Settings {
public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture";
/**
+ * Gesture that wakes up the display on quick pickup, toggling between
+ * {@link Display.STATE_OFF} and {@link Display.STATE_DOZE}.
+ * @hide
+ */
+ public static final String DOZE_QUICK_PICKUP_GESTURE = "doze_quick_pickup_gesture";
+
+ /**
* Whether the device should suppress the current doze configuration and disable dozing.
* @hide
*/
@@ -8471,7 +8480,6 @@ public final class Settings {
* For user preference if swipe bottom to expand notification gesture enabled.
* @hide
*/
- @Readable
public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
"swipe_bottom_to_notification_enabled";
@@ -8479,28 +8487,24 @@ public final class Settings {
* For user preference if One-Handed Mode enabled.
* @hide
*/
- @Readable
public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
/**
* For user preference if One-Handed Mode timeout.
* @hide
*/
- @Readable
public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
/**
* For user taps app to exit One-Handed Mode.
* @hide
*/
- @Readable
public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
/**
* Internal use, one handed mode tutorial showed times.
* @hide
*/
- @Readable
public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT =
"one_handed_tutorial_show_count";
@@ -8526,7 +8530,6 @@ public final class Settings {
* The last computed night mode bool the last time the phone was on
* @hide
*/
- @Readable
public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed";
/**
@@ -8945,7 +8948,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled";
/**
@@ -8953,7 +8955,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
"emergency_gesture_sound_enabled";
@@ -9686,7 +9687,6 @@ public final class Settings {
* @see Settings.Secure#MEDIA_CONTROLS_RESUME
* @hide
*/
- @Readable
public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
/**
@@ -9734,7 +9734,6 @@ public final class Settings {
* @hide
*/
@TestApi
- @Readable
public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
"accessibility_magnification_capability";
@@ -9744,7 +9743,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
"accessibility_show_window_magnification_prompt";
@@ -9761,7 +9759,6 @@ public final class Settings {
* @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
* @hide
*/
- @Readable
public static final String ACCESSIBILITY_BUTTON_MODE =
"accessibility_button_mode";
@@ -9790,7 +9787,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_SIZE =
"accessibility_floating_menu_size";
@@ -9803,7 +9799,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE =
"accessibility_floating_menu_icon_type";
@@ -9812,7 +9807,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED =
"accessibility_floating_menu_fade_enabled";
@@ -9822,7 +9816,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY =
"accessibility_floating_menu_opacity";
@@ -9831,7 +9824,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
/**
@@ -9855,7 +9847,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
"reminder_exp_learning_time_elapsed";
@@ -9864,11 +9855,18 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
"reminder_exp_learning_event_count";
/**
+ * Whether to show clipboard access notifications.
+ *
+ * @hide
+ */
+ public static final String CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS =
+ "clipboard_show_access_notifications";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -10565,7 +10563,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH =
"wm_display_settings_path";
@@ -10745,7 +10742,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP =
"hdmi_control_send_standby_on_sleep";
@@ -14423,7 +14419,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
"euicc_switch_slot_timeout_millis";
@@ -14433,7 +14428,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
"enable_multi_slot_timeout_millis";
@@ -14557,7 +14551,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
"force_non_debuggable_final_build_for_compat";
@@ -14637,7 +14630,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
/**
@@ -15320,7 +15312,6 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
- @Readable
public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled";
/**
@@ -15563,7 +15554,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist";
/**
@@ -15768,7 +15758,6 @@ public final class Settings {
* 1: Enabled
* @hide
*/
- @Readable
public static final String SHOW_PEOPLE_SPACE = "show_people_space";
/**
@@ -15779,7 +15768,6 @@ public final class Settings {
* 2: All conversations
* @hide
*/
- @Readable
public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
"people_space_conversation_type";
@@ -15790,7 +15778,6 @@ public final class Settings {
* 1: Enabled
* @hide
*/
- @Readable
public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss";
/**
@@ -15805,7 +15792,6 @@ public final class Settings {
* 1: Enabled (All apps will receive the new rules)
* @hide
*/
- @Readable
public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules";
/**
@@ -15820,7 +15806,6 @@ public final class Settings {
* <p>See {@link android.app.Notification.DevFlags} for more details.
* @hide
*/
- @Readable
public static final String FULLY_CUSTOM_VIEW_NOTIF_DECORATION =
"fully_custom_view_notif_decoration";
@@ -15834,7 +15819,6 @@ public final class Settings {
* <p>See {@link android.app.Notification.DevFlags} for more details.
* @hide
*/
- @Readable
public static final String DECORATED_CUSTOM_VIEW_NOTIF_DECORATION =
"decorated_custom_view_notif_decoration";
@@ -15851,7 +15835,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
/**
@@ -15877,7 +15860,6 @@ public final class Settings {
*
* @hide
*/
- @Readable
public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
"maximum_obscuring_opacity_for_touch";
@@ -15890,7 +15872,6 @@ public final class Settings {
* 1: enabled
* @hide
*/
- @Readable
public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 8a4812a42c8a..374de9ccb2f4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5310,5 +5310,12 @@ public final class Telephony {
* @hide
*/
public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+
+ /**
+ * TelephonyProvider column name for device to device sharing status.
+ *
+ * @hide
+ */
+ public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
}
}
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 4c8ee598f512..00c30b12c93d 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -23,6 +23,7 @@ import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -38,6 +39,7 @@ import android.util.Log;
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.XmlUtils;
@@ -233,6 +235,39 @@ public final class AutofillServiceInfo {
return compatibilityPackages;
}
+ /**
+ * Used by {@link TestDataBuilder}.
+ */
+ private AutofillServiceInfo(String passwordsActivity) {
+ mServiceInfo = new ServiceInfo();
+ mServiceInfo.applicationInfo = new ApplicationInfo();
+ mServiceInfo.packageName = "com.android.test";
+ mSettingsActivity = null;
+ mPasswordsActivity = passwordsActivity;
+ mCompatibilityPackages = null;
+ mInlineSuggestionsEnabled = false;
+ }
+
+ /**
+ * Builds test data for unit tests.
+ */
+ @VisibleForTesting
+ public static final class TestDataBuilder {
+ private String mPasswordsActivity;
+
+ public TestDataBuilder() {
+ }
+
+ public TestDataBuilder setPasswordsActivity(String passwordsActivity) {
+ mPasswordsActivity = passwordsActivity;
+ return this;
+ }
+
+ public AutofillServiceInfo build() {
+ return new AutofillServiceInfo(mPasswordsActivity);
+ }
+ }
+
@NonNull
public ServiceInfo getServiceInfo() {
return mServiceInfo;
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index e3d0741b4603..6147c58867a6 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -107,10 +107,12 @@ public final class Dataset implements Parcelable {
private final ArrayList<AutofillValue> mFieldValues;
private final ArrayList<RemoteViews> mFieldPresentations;
private final ArrayList<InlinePresentation> mFieldInlinePresentations;
+ private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
private final ArrayList<DatasetFieldFilter> mFieldFilters;
@Nullable private final ClipData mFieldContent;
private final RemoteViews mPresentation;
@Nullable private final InlinePresentation mInlinePresentation;
+ @Nullable private final InlinePresentation mInlineTooltipPresentation;
private final IntentSender mAuthentication;
@Nullable String mId;
@@ -119,10 +121,12 @@ public final class Dataset implements Parcelable {
mFieldValues = builder.mFieldValues;
mFieldPresentations = builder.mFieldPresentations;
mFieldInlinePresentations = builder.mFieldInlinePresentations;
+ mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations;
mFieldFilters = builder.mFieldFilters;
mFieldContent = builder.mFieldContent;
mPresentation = builder.mPresentation;
mInlinePresentation = builder.mInlinePresentation;
+ mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
mAuthentication = builder.mAuthentication;
mId = builder.mId;
}
@@ -154,6 +158,14 @@ public final class Dataset implements Parcelable {
}
/** @hide */
+ public @Nullable InlinePresentation getFieldInlineTooltipPresentation(int index) {
+ final InlinePresentation inlineTooltipPresentation =
+ mFieldInlineTooltipPresentations.get(index);
+ return inlineTooltipPresentation != null
+ ? inlineTooltipPresentation : mInlineTooltipPresentation;
+ }
+
+ /** @hide */
public @Nullable DatasetFieldFilter getFilter(int index) {
return mFieldFilters.get(index);
}
@@ -209,6 +221,10 @@ public final class Dataset implements Parcelable {
if (mFieldInlinePresentations != null) {
builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size());
}
+ if (mFieldInlineTooltipPresentations != null) {
+ builder.append(", fieldInlineTooltipInlinePresentations=").append(
+ mFieldInlineTooltipPresentations.size());
+ }
if (mFieldFilters != null) {
builder.append(", fieldFilters=").append(mFieldFilters.size());
}
@@ -218,6 +234,9 @@ public final class Dataset implements Parcelable {
if (mInlinePresentation != null) {
builder.append(", hasInlinePresentation");
}
+ if (mInlineTooltipPresentation != null) {
+ builder.append(", hasInlineTooltipPresentation");
+ }
if (mAuthentication != null) {
builder.append(", hasAuthentication");
}
@@ -245,10 +264,12 @@ public final class Dataset implements Parcelable {
private ArrayList<AutofillValue> mFieldValues;
private ArrayList<RemoteViews> mFieldPresentations;
private ArrayList<InlinePresentation> mFieldInlinePresentations;
+ private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
private ArrayList<DatasetFieldFilter> mFieldFilters;
@Nullable private ClipData mFieldContent;
private RemoteViews mPresentation;
@Nullable private InlinePresentation mInlinePresentation;
+ @Nullable private InlinePresentation mInlineTooltipPresentation;
private IntentSender mAuthentication;
private boolean mDestroyed;
@Nullable private String mId;
@@ -306,6 +327,31 @@ public final class Dataset implements Parcelable {
}
/**
+ * Visualizes this dataset as inline suggestions.
+ *
+ * @param inlinePresentation the {@link InlinePresentation} used to visualize this
+ * dataset as inline suggestions. If the dataset supports inline suggestions this
+ * should not be null.
+ * @param inlineTooltipPresentation the {@link InlinePresentation} used to show
+ * the tooltip for the {@code inlinePresentation}.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setInlinePresentation(
+ @NonNull InlinePresentation inlinePresentation,
+ @NonNull InlinePresentation inlineTooltipPresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+ Preconditions.checkNotNull(inlineTooltipPresentation,
+ "inlineTooltipPresentation must be non-null");
+ mInlinePresentation = inlinePresentation;
+ mInlineTooltipPresentation = inlineTooltipPresentation;
+ return this;
+ }
+
+ /**
* Triggers a custom UI before before autofilling the screen with the contents of this
* dataset.
*
@@ -598,6 +644,41 @@ public final class Dataset implements Parcelable {
/**
* Sets the value of a field, using a custom {@link RemoteViews presentation} to
+ * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion.
+ *
+ * @see #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param presentation the presentation used to visualize this field.
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions,
+ * this should not be null.
+ * @param inlineTooltipPresentation The {@link InlinePresentation} used to show
+ * the tooltip for the {@code inlinePresentation}.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+ @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
+ @NonNull InlinePresentation inlineTooltipPresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Preconditions.checkNotNull(inlineTooltipPresentation,
+ "inlineTooltipPresentation cannot be null");
+ setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
+ inlineTooltipPresentation, null);
+ return this;
+ }
+
+ /**
+ * Sets the value of a field, using a custom {@link RemoteViews presentation} to
* visualize it and a <a href="#Filtering">explicit filter</a>, and an
* {@link InlinePresentation} to visualize it as an inline suggestion.
*
@@ -641,6 +722,47 @@ public final class Dataset implements Parcelable {
}
/**
+ * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+ * visualize it and a <a href="#Filtering">explicit filter</a>, and an
+ * {@link InlinePresentation} to visualize it as an inline suggestion.
+ *
+ * @see #setValue(AutofillId, AutofillValue, Pattern, RemoteViews, InlinePresentation)
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ * @param presentation the presentation used to visualize this field.
+ * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+ * as inline suggestions. If the dataset supports inline suggestions, this
+ * should not be null.
+ * @param inlineTooltipPresentation The {@link InlinePresentation} used to show
+ * the tooltip for the {@code inlinePresentation}.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+ @Nullable Pattern filter, @NonNull RemoteViews presentation,
+ @NonNull InlinePresentation inlinePresentation,
+ @NonNull InlinePresentation inlineTooltipPresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Preconditions.checkNotNull(inlineTooltipPresentation,
+ "inlineTooltipPresentation cannot be null");
+ setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
+ inlineTooltipPresentation, new DatasetFieldFilter(filter));
+ return this;
+ }
+
+ /**
* Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an
* {@link InlinePresentation} to visualize it as an inline suggestion.
*
@@ -680,6 +802,15 @@ public final class Dataset implements Parcelable {
@Nullable AutofillValue value, @Nullable RemoteViews presentation,
@Nullable InlinePresentation inlinePresentation,
@Nullable DatasetFieldFilter filter) {
+ setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null,
+ filter);
+ }
+
+ private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
+ @Nullable AutofillValue value, @Nullable RemoteViews presentation,
+ @Nullable InlinePresentation inlinePresentation,
+ @Nullable InlinePresentation tooltip,
+ @Nullable DatasetFieldFilter filter) {
Preconditions.checkNotNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
@@ -687,6 +818,7 @@ public final class Dataset implements Parcelable {
mFieldValues.set(existingIdx, value);
mFieldPresentations.set(existingIdx, presentation);
mFieldInlinePresentations.set(existingIdx, inlinePresentation);
+ mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
mFieldFilters.set(existingIdx, filter);
return;
}
@@ -695,12 +827,14 @@ public final class Dataset implements Parcelable {
mFieldValues = new ArrayList<>();
mFieldPresentations = new ArrayList<>();
mFieldInlinePresentations = new ArrayList<>();
+ mFieldInlineTooltipPresentations = new ArrayList<>();
mFieldFilters = new ArrayList<>();
}
mFieldIds.add(id);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
mFieldInlinePresentations.add(inlinePresentation);
+ mFieldInlineTooltipPresentations.add(tooltip);
mFieldFilters.add(filter);
}
@@ -755,10 +889,12 @@ public final class Dataset implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelable(mInlinePresentation, flags);
+ parcel.writeParcelable(mInlineTooltipPresentation, flags);
parcel.writeTypedList(mFieldIds, flags);
parcel.writeTypedList(mFieldValues, flags);
parcel.writeTypedList(mFieldPresentations, flags);
parcel.writeTypedList(mFieldInlinePresentations, flags);
+ parcel.writeTypedList(mFieldInlineTooltipPresentations, flags);
parcel.writeTypedList(mFieldFilters, flags);
parcel.writeParcelable(mFieldContent, flags);
parcel.writeParcelable(mAuthentication, flags);
@@ -770,6 +906,8 @@ public final class Dataset implements Parcelable {
public Dataset createFromParcel(Parcel parcel) {
final RemoteViews presentation = parcel.readParcelable(null);
final InlinePresentation inlinePresentation = parcel.readParcelable(null);
+ final InlinePresentation inlineTooltipPresentation =
+ parcel.readParcelable(null);
final ArrayList<AutofillId> ids =
parcel.createTypedArrayList(AutofillId.CREATOR);
final ArrayList<AutofillValue> values =
@@ -778,6 +916,8 @@ public final class Dataset implements Parcelable {
parcel.createTypedArrayList(RemoteViews.CREATOR);
final ArrayList<InlinePresentation> inlinePresentations =
parcel.createTypedArrayList(InlinePresentation.CREATOR);
+ final ArrayList<InlinePresentation> inlineTooltipPresentations =
+ parcel.createTypedArrayList(InlinePresentation.CREATOR);
final ArrayList<DatasetFieldFilter> filters =
parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
final ClipData fieldContent = parcel.readParcelable(null);
@@ -790,8 +930,13 @@ public final class Dataset implements Parcelable {
final Builder builder = (presentation != null) ? new Builder(presentation)
: new Builder();
if (inlinePresentation != null) {
- builder.setInlinePresentation(inlinePresentation);
+ if (inlineTooltipPresentation != null) {
+ builder.setInlinePresentation(inlinePresentation, inlineTooltipPresentation);
+ } else {
+ builder.setInlinePresentation(inlinePresentation);
+ }
}
+
if (fieldContent != null) {
builder.setContent(ids.get(0), fieldContent);
}
@@ -802,9 +947,11 @@ public final class Dataset implements Parcelable {
final RemoteViews fieldPresentation = presentations.get(i);
final InlinePresentation fieldInlinePresentation =
i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
+ final InlinePresentation fieldInlineTooltipPresentation =
+ i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
final DatasetFieldFilter filter = filters.get(i);
builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
- fieldInlinePresentation, filter);
+ fieldInlinePresentation, fieldInlineTooltipPresentation, filter);
}
builder.setAuthentication(authentication);
builder.setId(datasetId);
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index b1107a8c2efb..740ae260999f 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -23,6 +23,7 @@ import static android.view.autofill.Helper.sDebug;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.Activity;
import android.content.IntentSender;
@@ -76,6 +77,7 @@ public final class FillResponse implements Parcelable {
private final @Nullable Bundle mClientState;
private final @Nullable RemoteViews mPresentation;
private final @Nullable InlinePresentation mInlinePresentation;
+ private final @Nullable InlinePresentation mInlineTooltipPresentation;
private final @Nullable RemoteViews mHeader;
private final @Nullable RemoteViews mFooter;
private final @Nullable IntentSender mAuthentication;
@@ -95,6 +97,7 @@ public final class FillResponse implements Parcelable {
mClientState = builder.mClientState;
mPresentation = builder.mPresentation;
mInlinePresentation = builder.mInlinePresentation;
+ mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
mHeader = builder.mHeader;
mFooter = builder.mFooter;
mAuthentication = builder.mAuthentication;
@@ -135,6 +138,11 @@ public final class FillResponse implements Parcelable {
}
/** @hide */
+ public @Nullable InlinePresentation getInlineTooltipPresentation() {
+ return mInlineTooltipPresentation;
+ }
+
+ /** @hide */
public @Nullable RemoteViews getHeader() {
return mHeader;
}
@@ -219,6 +227,7 @@ public final class FillResponse implements Parcelable {
private Bundle mClientState;
private RemoteViews mPresentation;
private InlinePresentation mInlinePresentation;
+ private InlinePresentation mInlineTooltipPresentation;
private RemoteViews mHeader;
private RemoteViews mFooter;
private IntentSender mAuthentication;
@@ -360,6 +369,22 @@ public final class FillResponse implements Parcelable {
public Builder setAuthentication(@NonNull AutofillId[] ids,
@Nullable IntentSender authentication, @Nullable RemoteViews presentation,
@Nullable InlinePresentation inlinePresentation) {
+ return setAuthentication(ids, authentication, presentation, inlinePresentation, null);
+ }
+
+ /**
+ * Triggers a custom UI before before autofilling the screen with any data set in this
+ * response.
+ *
+ * <p>This method like
+ * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)}
+ * but allows setting an {@link InlinePresentation} for the inline suggestion tooltip.
+ */
+ @NonNull
+ public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
+ @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
+ @Nullable InlinePresentation inlinePresentation,
+ @Nullable InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
throwIfDisableAutofillCalled();
if (mHeader != null || mFooter != null) {
@@ -373,6 +398,7 @@ public final class FillResponse implements Parcelable {
mAuthentication = authentication;
mPresentation = presentation;
mInlinePresentation = inlinePresentation;
+ mInlineTooltipPresentation = inlineTooltipPresentation;
mAuthenticationIds = assertValid(ids);
return this;
}
@@ -737,6 +763,9 @@ public final class FillResponse implements Parcelable {
if (mInlinePresentation != null) {
builder.append(", hasInlinePresentation");
}
+ if (mInlineTooltipPresentation != null) {
+ builder.append(", hasInlineTooltipPresentation");
+ }
if (mHeader != null) {
builder.append(", hasHeader");
}
@@ -784,6 +813,7 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelable(mAuthentication, flags);
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelable(mInlinePresentation, flags);
+ parcel.writeParcelable(mInlineTooltipPresentation, flags);
parcel.writeParcelable(mHeader, flags);
parcel.writeParcelable(mFooter, flags);
parcel.writeParcelable(mUserData, flags);
@@ -818,9 +848,10 @@ public final class FillResponse implements Parcelable {
final IntentSender authentication = parcel.readParcelable(null);
final RemoteViews presentation = parcel.readParcelable(null);
final InlinePresentation inlinePresentation = parcel.readParcelable(null);
+ final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null);
if (authenticationIds != null) {
builder.setAuthentication(authenticationIds, authentication, presentation,
- inlinePresentation);
+ inlinePresentation, inlineTooltipPresentation);
}
final RemoteViews header = parcel.readParcelable(null);
if (header != null) {
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index fbf23b69addf..40349576c460 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -75,9 +75,23 @@ public final class InlinePresentation implements Parcelable {
return hints.toArray(new String[hints.size()]);
}
+ /**
+ * Creates a presentation for the inline suggestion tooltip
+ *
+ * @param slice Represents the UI content and the action for the inline suggestion tooltip.
+ * @param spec Specifies the UI specification for the inline suggestion tooltip.
+ * @return An {@link InlinePresentation} for the inline suggestion tooltip
+ */
+ @NonNull
+ public static InlinePresentation createTooltipPresentation(@NonNull Slice slice,
+ @NonNull InlinePresentationSpec spec) {
+ return new InlinePresentation(slice, spec, /* pinned */ false);
+
+ }
+
- // Code below generated by codegen v1.0.20.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -259,10 +273,10 @@ public final class InlinePresentation implements Parcelable {
};
@DataClass.Generated(
- time = 1604456277638L,
- codegenVersion = "1.0.20",
+ time = 1615968415006L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
- inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size java.lang.String[] getAutofillHints()\npublic static @android.annotation.NonNull android.service.autofill.InlinePresentation createTooltipPresentation(android.app.slice.Slice,android.widget.inline.InlinePresentationSpec)\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/displayhash/DisplayHashParams.aidl b/core/java/android/service/displayhash/DisplayHashParams.aidl
new file mode 100644
index 000000000000..90f9bf14a630
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHashParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.displayhash;
+
+parcelable DisplayHashParams;
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
new file mode 100644
index 000000000000..6a176a338d2e
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.displayhash;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Parcelable;
+import android.util.Size;
+import android.view.displayhash.DisplayHashResultCallback;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Information passed from the {@link DisplayHasherService} to system server about how to get the
+ * display data that will be used to generate the {@link android.view.displayhash.DisplayHash}
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(genAidl = true, genToString = true, genParcelable = true, genHiddenConstructor = true)
+public final class DisplayHashParams implements Parcelable {
+ /**
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+ * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+ * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+ * If {@code null}, the buffer size will not be changed.
+ */
+ @Nullable
+ private final Size mBufferSize;
+
+ /**
+ * Whether the content captured will use filtering when scaling.
+ */
+ private final boolean mBufferScaleWithFiltering;
+
+ /**
+ * Whether the content will be captured in grayscale or color.
+ */
+ private final boolean mGrayscaleBuffer;
+
+ /**
+ * A builder for {@link DisplayHashParams}
+ */
+ public static final class Builder {
+ @Nullable
+ private Size mBufferSize;
+ private boolean mBufferScaleWithFiltering;
+ private boolean mGrayscaleBuffer;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash.
+ */
+ @NonNull
+ public Builder setBufferSize(int w, int h) {
+ mBufferSize = new Size(w, h);
+ return this;
+ }
+
+ /**
+ * Whether the content captured will use filtering when scaling.
+ */
+ @NonNull
+ public Builder setBufferScaleWithFiltering(boolean value) {
+ mBufferScaleWithFiltering = value;
+ return this;
+ }
+
+ /**
+ * Whether the content will be captured in grayscale or color.
+ */
+ @NonNull
+ public Builder setGrayscaleBuffer(boolean value) {
+ mGrayscaleBuffer = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ @NonNull
+ public DisplayHashParams build() {
+ return new DisplayHashParams(mBufferSize, mBufferScaleWithFiltering, mGrayscaleBuffer);
+ }
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DisplayHashParams.
+ *
+ * @param bufferSize
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+ * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+ * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+ * If {@code null}, the buffer size will not be changed.
+ * @param bufferScaleWithFiltering
+ * Whether the content captured will use filtering when scaling.
+ * @param grayscaleBuffer
+ * Whether the content will be captured in grayscale or color.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public DisplayHashParams(
+ @Nullable Size bufferSize,
+ boolean bufferScaleWithFiltering,
+ boolean grayscaleBuffer) {
+ this.mBufferSize = bufferSize;
+ this.mBufferScaleWithFiltering = bufferScaleWithFiltering;
+ this.mGrayscaleBuffer = grayscaleBuffer;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+ * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+ * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+ * If {@code null}, the buffer size will not be changed.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Size getBufferSize() {
+ return mBufferSize;
+ }
+
+ /**
+ * Whether the content captured will use filtering when scaling.
+ */
+ @DataClass.Generated.Member
+ public boolean isBufferScaleWithFiltering() {
+ return mBufferScaleWithFiltering;
+ }
+
+ /**
+ * Whether the content will be captured in grayscale or color.
+ */
+ @DataClass.Generated.Member
+ public boolean isGrayscaleBuffer() {
+ return mGrayscaleBuffer;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DisplayHashParams { " +
+ "bufferSize = " + mBufferSize + ", " +
+ "bufferScaleWithFiltering = " + mBufferScaleWithFiltering + ", " +
+ "grayscaleBuffer = " + mGrayscaleBuffer +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mBufferScaleWithFiltering) flg |= 0x2;
+ if (mGrayscaleBuffer) flg |= 0x4;
+ if (mBufferSize != null) flg |= 0x1;
+ dest.writeByte(flg);
+ if (mBufferSize != null) dest.writeSize(mBufferSize);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ DisplayHashParams(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ boolean bufferScaleWithFiltering = (flg & 0x2) != 0;
+ boolean grayscaleBuffer = (flg & 0x4) != 0;
+ Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
+
+ this.mBufferSize = bufferSize;
+ this.mBufferScaleWithFiltering = bufferScaleWithFiltering;
+ this.mGrayscaleBuffer = grayscaleBuffer;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<DisplayHashParams> CREATOR
+ = new Parcelable.Creator<DisplayHashParams>() {
+ @Override
+ public DisplayHashParams[] newArray(int size) {
+ return new DisplayHashParams[size];
+ }
+
+ @Override
+ public DisplayHashParams createFromParcel(@NonNull android.os.Parcel in) {
+ return new DisplayHashParams(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1615565493989L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
+ inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mBufferScaleWithFiltering\nprivate final boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mBufferScaleWithFiltering\nprivate boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferScaleWithFiltering(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 331dbe91f6c7..2105d848057e 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -34,6 +34,8 @@ import android.view.displayhash.DisplayHash;
import android.view.displayhash.DisplayHashResultCallback;
import android.view.displayhash.VerifiedDisplayHash;
+import java.util.Map;
+
/**
* A service that handles generating and verify {@link DisplayHash}.
*
@@ -50,15 +52,6 @@ public abstract class DisplayHasherService extends Service {
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
- * Manifest metadata key for the resource string array containing the names of all hashing
- * algorithms provided by the service.
- *
- * @hide
- */
- public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
- "android.displayhash.available_algorithms";
-
- /**
* The {@link Intent} action that must be declared as handled by a service in its manifest
* for the system to recognize it as a DisplayHash providing service.
*
@@ -96,7 +89,7 @@ public abstract class DisplayHasherService extends Service {
* @param buffer The buffer for the content to generate the hash for.
* @param bounds The size and position of the content in window space.
* @param hashAlgorithm The String for the hashing algorithm to use based values in
- * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+ * {@link #getDisplayHashAlgorithms(RemoteCallback)}.
* @param callback The callback to invoke
* {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)}
* if successfully generated a DisplayHash or {@link
@@ -108,6 +101,12 @@ public abstract class DisplayHasherService extends Service {
@NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
/**
+ * Returns a map of supported algorithms and their {@link DisplayHashParams}
+ */
+ @NonNull
+ public abstract Map<String, DisplayHashParams> onGetDisplayHashAlgorithms();
+
+ /**
* Call to verify that the DisplayHash passed in was generated by the system.
*
* @param salt The salt value to use when verifying the hmac. This should be the
@@ -132,6 +131,15 @@ public abstract class DisplayHasherService extends Service {
callback.sendResult(data);
}
+ private void getDisplayHashAlgorithms(RemoteCallback callback) {
+ Map<String, DisplayHashParams> displayHashParams = onGetDisplayHashAlgorithms();
+ final Bundle data = new Bundle();
+ for (Map.Entry<String, DisplayHashParams> entry : displayHashParams.entrySet()) {
+ data.putParcelable(entry.getKey(), entry.getValue());
+ }
+ callback.sendResult(data);
+ }
+
private final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub {
@Override
public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
@@ -164,5 +172,11 @@ public abstract class DisplayHasherService extends Service {
obtainMessage(DisplayHasherService::verifyDisplayHash,
DisplayHasherService.this, salt, displayHash, callback));
}
+
+ @Override
+ public void getDisplayHashAlgorithms(RemoteCallback callback) {
+ mHandler.sendMessage(obtainMessage(DisplayHasherService::getDisplayHashAlgorithms,
+ DisplayHasherService.this, callback));
+ }
}
}
diff --git a/core/java/android/service/displayhash/IDisplayHasherService.aidl b/core/java/android/service/displayhash/IDisplayHasherService.aidl
index 236bc28c74c8..d9dcdcaf921e 100644
--- a/core/java/android/service/displayhash/IDisplayHasherService.aidl
+++ b/core/java/android/service/displayhash/IDisplayHasherService.aidl
@@ -51,4 +51,11 @@ oneway interface IDisplayHasherService {
* @param callback The callback invoked to send back the VerifiedDisplayHash.
*/
void verifyDisplayHash(in byte[] salt, in DisplayHash displayHash, in RemoteCallback callback);
+
+ /**
+ * Call to get a map of supported algorithms and their {@link DisplayHashParams}
+ *
+ * @param callback The callback invoked to send back the map of algorithms to DisplayHashParams.
+ */
+ void getDisplayHashAlgorithms(in RemoteCallback callback);
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7aa5bbc930fb..bf5f24d17f8a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1786,7 +1786,8 @@ public abstract class NotificationListenerService extends Service {
* {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
* no such preference has been expressed.
*/
- public int getLockscreenVisibilityOverride() {
+ public @Notification.NotificationVisibilityOverride
+ int getLockscreenVisibilityOverride() {
return mVisibilityOverride;
}
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 8e76e2fc9202..8dec0922b097 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -16,12 +16,15 @@
package android.service.rotationresolver;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
+import com.android.internal.util.DataClass;
+
/**
* This class represents a request to an {@link RotationResolverService}. The request contains
* information from the system that can help RotationResolverService to determine the appropriate
@@ -33,68 +36,209 @@ import android.view.Surface;
* @hide
*/
@SystemApi
+@DataClass (
+ genParcelable = true,
+ genToString = true
+)
public final class RotationResolutionRequest implements Parcelable {
- private final @NonNull String mPackageName;
- private final int mProposedRotation;
- private final int mCurrentRotation;
- private final long mTimeoutMillis;
+ /** The Name of the package of the current fore-ground activity. */
+ @NonNull private final String mPackageName;
+
+ /** The current rotation of the screen. */
+ @Surface.Rotation private final int mCurrentRotation;
+
+ /** The proposed screen rotation in the system. */
+ @Surface.Rotation private final int mProposedRotation;
+
+ /** Whether should use camera signal to resolver rotation. */
+ private final boolean mShouldUseCamera;
+
+ /** The timeout of the request. */
+ @DurationMillisLong private final long mTimeoutMillis;
+
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
/**
- * @param proposedRotation The system proposed screen rotation.
- * @param currentRotation The current screen rotation of the phone.
- * @param packageName The current package name of the activity that is running in
- * foreground.
- * @param timeoutMillis The timeout in millisecond for the rotation request.
- * @hide
+ * Creates a new RotationResolutionRequest.
+ *
+ * @param packageName
+ * The Name of the package of the current fore-ground activity.
+ * @param currentRotation
+ * The current rotation of the screen.
+ * @param proposedRotation
+ * The proposed screen rotation in the system.
+ * @param shouldUseCamera
+ * Whether should use camera signal to resolver rotation.
+ * @param timeoutMillis
+ * The timeout of the request.
*/
- public RotationResolutionRequest(int proposedRotation, int currentRotation,
- @NonNull String packageName, long timeoutMillis) {
- mProposedRotation = proposedRotation;
- mCurrentRotation = currentRotation;
- mPackageName = packageName;
- mTimeoutMillis = timeoutMillis;
+ @DataClass.Generated.Member
+ public RotationResolutionRequest(
+ @NonNull String packageName,
+ @Surface.Rotation int currentRotation,
+ @Surface.Rotation int proposedRotation,
+ boolean shouldUseCamera,
+ @DurationMillisLong long timeoutMillis) {
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mCurrentRotation = currentRotation;
+ com.android.internal.util.AnnotationValidations.validate(
+ Surface.Rotation.class, null, mCurrentRotation);
+ this.mProposedRotation = proposedRotation;
+ com.android.internal.util.AnnotationValidations.validate(
+ Surface.Rotation.class, null, mProposedRotation);
+ this.mShouldUseCamera = shouldUseCamera;
+ this.mTimeoutMillis = timeoutMillis;
+ com.android.internal.util.AnnotationValidations.validate(
+ DurationMillisLong.class, null, mTimeoutMillis);
+
+ // onConstructed(); // You can define this method to get a callback
}
- @Surface.Rotation public int getProposedRotation() {
- return mProposedRotation;
+ /**
+ * The Name of the package of the current fore-ground activity.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
}
- public int getCurrentRotation() {
+ /**
+ * The current rotation of the screen.
+ */
+ @DataClass.Generated.Member
+ public @Surface.Rotation int getCurrentRotation() {
return mCurrentRotation;
}
- public @NonNull String getPackageName() {
- return mPackageName;
+ /**
+ * The proposed screen rotation in the system.
+ */
+ @DataClass.Generated.Member
+ public @Surface.Rotation int getProposedRotation() {
+ return mProposedRotation;
}
- public long getTimeoutMillis() {
+ /**
+ * Whether should use camera signal to resolver rotation.
+ */
+ @DataClass.Generated.Member
+ public boolean shouldUseCamera() {
+ return mShouldUseCamera;
+ }
+
+ /**
+ * The timeout of the request.
+ */
+ @DataClass.Generated.Member
+ public @DurationMillisLong long getTimeoutMillis() {
return mTimeoutMillis;
}
@Override
- public int describeContents() {
- return 0;
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "RotationResolutionRequest { " +
+ "packageName = " + mPackageName + ", " +
+ "currentRotation = " + mCurrentRotation + ", " +
+ "proposedRotation = " + mProposedRotation + ", " +
+ "shouldUseCamera = " + mShouldUseCamera + ", " +
+ "timeoutMillis = " + mTimeoutMillis +
+ " }";
}
@Override
- public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mProposedRotation);
- parcel.writeInt(mCurrentRotation);
- parcel.writeString(mPackageName);
- parcel.writeLong(mTimeoutMillis);
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mShouldUseCamera) flg |= 0x8;
+ dest.writeByte(flg);
+ dest.writeString(mPackageName);
+ dest.writeInt(mCurrentRotation);
+ dest.writeInt(mProposedRotation);
+ dest.writeLong(mTimeoutMillis);
}
- public static final @NonNull Creator<RotationResolutionRequest> CREATOR =
- new Creator<RotationResolutionRequest>() {
- @Override
- public RotationResolutionRequest createFromParcel(Parcel source) {
- return new RotationResolutionRequest(source.readInt(), source.readInt(),
- source.readString(), source.readLong());
- }
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ RotationResolutionRequest(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
+ boolean shouldUseCamera = (flg & 0x8) != 0;
+ String packageName = in.readString();
+ int currentRotation = in.readInt();
+ int proposedRotation = in.readInt();
+ long timeoutMillis = in.readLong();
+
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mCurrentRotation = currentRotation;
+ com.android.internal.util.AnnotationValidations.validate(
+ Surface.Rotation.class, null, mCurrentRotation);
+ this.mProposedRotation = proposedRotation;
+ com.android.internal.util.AnnotationValidations.validate(
+ Surface.Rotation.class, null, mProposedRotation);
+ this.mShouldUseCamera = shouldUseCamera;
+ this.mTimeoutMillis = timeoutMillis;
+ com.android.internal.util.AnnotationValidations.validate(
+ DurationMillisLong.class, null, mTimeoutMillis);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<RotationResolutionRequest> CREATOR
+ = new Parcelable.Creator<RotationResolutionRequest>() {
@Override
public RotationResolutionRequest[] newArray(int size) {
return new RotationResolutionRequest[size];
}
+
+ @Override
+ public RotationResolutionRequest createFromParcel(@NonNull Parcel in) {
+ return new RotationResolutionRequest(in);
+ }
};
+
+ @DataClass.Generated(
+ time = 1615402421314L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/rotationresolver/RotationResolutionRequest.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.view.Surface.Rotation int mCurrentRotation\nprivate final @android.view.Surface.Rotation int mProposedRotation\nprivate final boolean mShouldUseCamera\nprivate final @android.annotation.DurationMillisLong long mTimeoutMillis\nclass RotationResolutionRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index d71a8300d9b8..a9348c68165d 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -218,7 +218,30 @@ public abstract class TimeZoneProviderService extends Service {
}
/**
- * Starts the provider sending updates.
+ * Informs the provider that it should start detecting and reporting the detected time zone
+ * state via the various {@code report} methods. Implementations of {@link
+ * #onStartUpdates(long)} should return immediately, and will typically be used to start
+ * worker threads or begin asynchronous location listening.
+ *
+ * <p>Between {@link #onStartUpdates(long)} and {@link #onStopUpdates()} calls, the Android
+ * system server holds the latest report from the provider in memory. After an initial report,
+ * provider implementations are only required to send a report via {@link
+ * #reportSuggestion(TimeZoneProviderSuggestion)} or via {@link #reportUncertain()} when it
+ * differs from the previous report.
+ *
+ * <p>{@link #reportPermanentFailure(Throwable)} can also be called by provider implementations
+ * in rare cases, after which the provider should consider itself stopped and not make any
+ * further reports. {@link #onStopUpdates()} will not be called in this case.
+ *
+ * <p>The {@code initializationTimeoutMillis} parameter indicates how long the provider has been
+ * granted to call one of the {@code report} methods for the first time. If the provider does
+ * not call one of the {@code report} methods in this time, it may be judged uncertain and the
+ * Android system server may move on to use other providers or detection methods. Providers
+ * should therefore make best efforts during this time to generate a report, which could involve
+ * increased power usage. Providers should preferably report an explicit {@link
+ * #reportUncertain()} if the time zone(s) cannot be detected within the initialization timeout.
+ *
+ * @see #onStopUpdates() for the signal from the system server to stop sending reports
*/
public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis);
@@ -228,7 +251,8 @@ public abstract class TimeZoneProviderService extends Service {
}
/**
- * Stops the provider sending updates.
+ * Stops the provider sending further updates. This will be called after {@link
+ * #onStartUpdates(long)}.
*/
public abstract void onStopUpdates();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index c1d9d5816c9d..def13db41559 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -41,10 +41,12 @@ import android.media.permission.Identity;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SharedMemory;
import android.util.Slog;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -287,6 +289,7 @@ public class AlwaysOnHotwordDetector {
private final Handler mHandler;
private final IBinder mBinder = new Binder();
private final int mTargetSdkVersion;
+ private final boolean mSupportHotwordDetectionService;
private int mAvailability = STATE_NOT_READY;
@@ -488,11 +491,22 @@ public class AlwaysOnHotwordDetector {
* @param callback A non-null Callback for receiving the recognition events.
* @param modelManagementService A service that allows management of sound models.
* @param targetSdkVersion The target SDK version.
+ * @param supportHotwordDetectionService {@code true} if hotword detection service should be
+ * triggered, otherwise {@code false}.
+ * @param options Application configuration data provided by the
+ * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+ * contents that can be used to communicate with other processes.
+ * @param sharedMemory The unrestricted data blob provided by the
+ * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * such data to the trusted process.
+ *
* @hide
*/
public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
- IVoiceInteractionManagerService modelManagementService, int targetSdkVersion) {
+ IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
+ boolean supportHotwordDetectionService, @Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory) {
mText = text;
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
@@ -501,6 +515,10 @@ public class AlwaysOnHotwordDetector {
mInternalCallback = new SoundTriggerListener(mHandler);
mModelManagementService = modelManagementService;
mTargetSdkVersion = targetSdkVersion;
+ mSupportHotwordDetectionService = supportHotwordDetectionService;
+ if (mSupportHotwordDetectionService) {
+ setHotwordDetectionServiceConfig(options, sharedMemory);
+ }
try {
Identity identity = new Identity();
identity.packageName = ActivityThread.currentOpPackageName();
@@ -513,6 +531,38 @@ public class AlwaysOnHotwordDetector {
}
/**
+ * Set configuration and pass read-only data to hotword detection service.
+ *
+ * @param options Application configuration data provided by the
+ * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+ * contents that can be used to communicate with other processes.
+ * @param sharedMemory The unrestricted data blob provided by the
+ * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * such data to the trusted process.
+ *
+ * @throws IllegalStateException if it doesn't support hotword detection service.
+ *
+ * @hide
+ */
+ public final void setHotwordDetectionServiceConfig(@Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory) {
+ if (DBG) {
+ Slog.d(TAG, "setHotwordDetectionServiceConfig()");
+ }
+ if (!mSupportHotwordDetectionService) {
+ throw new IllegalStateException(
+ "setHotwordDetectionServiceConfig called, but it doesn't support hotword"
+ + " detection service");
+ }
+
+ try {
+ mModelManagementService.setHotwordDetectionServiceConfig(options, sharedMemory);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the recognition modes supported by the associated keyphrase.
*
* @see #RECOGNITION_MODE_USER_IDENTIFICATION
@@ -839,6 +889,14 @@ public class AlwaysOnHotwordDetector {
synchronized (mLock) {
mAvailability = STATE_INVALID;
notifyStateChangedLocked();
+
+ if (mSupportHotwordDetectionService) {
+ try {
+ mModelManagementService.shutdownHotwordDetectionService();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 7f1c5ff96636..fcef26f13dd0 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -27,13 +27,17 @@ import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.media.AudioFormat;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SharedMemory;
import android.util.Log;
+import java.util.Locale;
+
/**
* Implemented by an application that wants to offer detection for hotword. The system will
* start the service after calling {@link VoiceInteractionService#setHotwordDetectionConfig}.
@@ -76,6 +80,17 @@ public abstract class HotwordDetectionService extends Service {
timeoutMillis,
new DspHotwordDetectionCallback(callback)));
}
+
+ @Override
+ public void setConfig(Bundle options, SharedMemory sharedMemory) throws RemoteException {
+ if (DBG) {
+ Log.d(TAG, "#setConfig");
+ }
+ mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
+ HotwordDetectionService.this,
+ options,
+ sharedMemory));
+ }
};
@CallSuper
@@ -121,6 +136,25 @@ public abstract class HotwordDetectionService extends Service {
}
/**
+ * Called when the {@link VoiceInteractionService#createAlwaysOnHotwordDetector(String, Locale,
+ * Bundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} or {@link AlwaysOnHotwordDetector#
+ * setHotwordDetectionServiceConfig(Bundle, SharedMemory)} requests an update of the hotword
+ * detection parameters.
+ *
+ * @param options Application configuration data provided by the
+ * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+ * contents that can be used to communicate with other processes.
+ * @param sharedMemory The unrestricted data blob provided by the
+ * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * such data to the trusted process.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onUpdateState(@Nullable Bundle options, @Nullable SharedMemory sharedMemory) {
+ }
+
+ /**
* Callback for returning the detected result.
*
* @hide
diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl
index cbe76e4bf69f..8f0874a5cb2e 100644
--- a/core/java/android/service/voice/IHotwordDetectionService.aidl
+++ b/core/java/android/service/voice/IHotwordDetectionService.aidl
@@ -17,7 +17,9 @@
package android.service.voice;
import android.media.AudioFormat;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
import android.service.voice.IDspHotwordDetectionCallback;
/**
@@ -31,4 +33,6 @@ oneway interface IHotwordDetectionService {
in AudioFormat audioFormat,
long timeoutMillis,
in IDspHotwordDetectionCallback callback);
+
+ void setConfig(in Bundle options, in SharedMemory sharedMemory);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 25f80900f1cf..9ba39a1b37f7 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -17,7 +17,6 @@
package android.service.voice;
import android.Manifest;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -36,6 +35,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SharedMemory;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;
@@ -47,8 +47,6 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -73,32 +71,6 @@ public class VoiceInteractionService extends Service {
static final String TAG = VoiceInteractionService.class.getSimpleName();
/**
- * Indicates that the given configs have been set successfully after calling
- * {@link VoiceInteractionService#setHotwordDetectionConfig}.
- *
- * @hide
- */
- @SystemApi
- public static final int HOTWORD_CONFIG_SUCCESS = 0;
-
- /**
- * Indicates that the given configs have been set unsuccessfully after calling
- * {@link VoiceInteractionService#setHotwordDetectionConfig}.
- *
- * @hide
- */
- @SystemApi
- public static final int HOTWORD_CONFIG_FAILURE = 1;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "HOTWORD_CONFIG_" }, value = {
- HOTWORD_CONFIG_SUCCESS,
- HOTWORD_CONFIG_FAILURE,
- })
- public @interface HotwordConfigResult {}
-
- /**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
* {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
@@ -330,52 +302,76 @@ public class VoiceInteractionService extends Service {
}
/**
- * Set hotword detection configuration.
- *
- * Note: Currently it will trigger hotword detection service after calling this function when
- * all conditions meet the requirements.
- *
- * @param options Config data.
- * @return {@link VoiceInteractionService#HOTWORD_CONFIG_SUCCESS} in case of success,
- * {@link VoiceInteractionService#HOTWORD_CONFIG_FAILURE} in case of failure.
+ * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
+ * This instance must be retained and used by the client.
+ * Calling this a second time invalidates the previously created hotword detector
+ * which can no longer be used to manage recognition.
*
- * @throws IllegalStateException if the function is called before onReady() is called.
+ * @param keyphrase The keyphrase that's being used, for example "Hello Android".
+ * @param locale The locale for which the enrollment needs to be performed.
+ * @param callback The callback to notify of detection events.
+ * @return An always-on hotword detector for the given keyphrase and locale.
*
* @hide
*/
@SystemApi
- @HotwordConfigResult
- public final int setHotwordDetectionConfig(
- @SuppressLint("NullableCollection") @Nullable Bundle options) {
- if (mSystemService == null) {
- throw new IllegalStateException("Not available until onReady() is called");
- }
-
- try {
- return mSystemService.setHotwordDetectionConfig(options);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ @NonNull
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
+ @SuppressLint("MissingNullability") String keyphrase, // TODO: nullability properly
+ @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+ @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+ return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+ /* supportHotwordDetectionService= */ false, /* options= */ null,
+ /* sharedMemory= */ null, callback);
}
/**
- * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
- * This instance must be retained and used by the client.
- * Calling this a second time invalidates the previously created hotword detector
- * which can no longer be used to manage recognition.
+ * Create an {@link AlwaysOnHotwordDetector} and trigger a {@link HotwordDetectionService}
+ * service, then it will also pass the read-only data to hotword detection service.
+ *
+ * Like {@see #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)
+ * }. Before calling this function, you should set a valid hotword detection service with
+ * android:hotwordDetectionService in an android.voice_interaction metadata file and set
+ * android:isolatedProcess="true" in the AndroidManifest.xml of hotword detection service.
+ * Otherwise it will throw IllegalStateException. After calling this function, the system will
+ * also trigger a hotword detection service and pass the read-only data back to it.
+ *
+ * <p>Note: The system will trigger hotword detection service after calling this function when
+ * all conditions meet the requirements.
*
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
+ * @param options Application configuration data provided by the
+ * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+ * contents that can be used to communicate with other processes.
+ * @param sharedMemory The unrestricted data blob provided by the
+ * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * such data to the trusted process.
* @param callback The callback to notify of detection events.
* @return An always-on hotword detector for the given keyphrase and locale.
*
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
@NonNull
public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
- @SuppressLint("MissingNullability") String keyphrase, // TODO: annotate nullability properly
+ @SuppressLint("MissingNullability") String keyphrase, // TODO: nullability properly
+ @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+ @Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory,
+ @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+ return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+ /* supportHotwordDetectionService= */ true, options,
+ sharedMemory, callback);
+ }
+
+ private AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorInternal(
+ @SuppressLint("MissingNullability") String keyphrase, // TODO: nullability properly
@SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+ boolean supportHotwordDetectionService,
+ @Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory,
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
@@ -385,7 +381,8 @@ public class VoiceInteractionService extends Service {
safelyShutdownHotwordDetector();
mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
mKeyphraseEnrollmentInfo, mSystemService,
- getApplicationContext().getApplicationInfo().targetSdkVersion);
+ getApplicationContext().getApplicationInfo().targetSdkVersion,
+ supportHotwordDetectionService, options, sharedMemory);
}
return mHotwordDetector;
}
@@ -432,7 +429,6 @@ public class VoiceInteractionService extends Service {
}
private void safelyShutdownHotwordDetector() {
- // TODO (b/178171906): Need to check if the HotwordDetectionService should be unbound.
synchronized (mLock) {
if (mHotwordDetector == null) {
return;
diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl
index 8e5292d1ddf1..ad402262878d 100644
--- a/core/java/android/speech/IRecognitionServiceManager.aidl
+++ b/core/java/android/speech/IRecognitionServiceManager.aidl
@@ -31,4 +31,6 @@ oneway interface IRecognitionServiceManager {
in IBinder clientToken,
boolean onDevice,
in IRecognitionServiceManagerCallback callback);
+
+ void setTemporaryComponent(in ComponentName componentName);
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 850f997a2d2f..9b93a64e48a3 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -17,6 +17,8 @@
package android.speech;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -35,6 +37,8 @@ import android.util.Log;
import android.util.Slog;
import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* This class provides access to the speech recognition service. This service allows access to the
@@ -52,7 +56,7 @@ import java.util.List;
*/
public class SpeechRecognizer {
/** DEBUG value to enable verbose debug prints */
- private final static boolean DBG = false;
+ private static final boolean DBG = false;
/** Log messages identifier */
private static final String TAG = "SpeechRecognizer";
@@ -113,10 +117,11 @@ public class SpeechRecognizer {
public static final int ERROR_SERVER_DISCONNECTED = 11;
/** action codes */
- private final static int MSG_START = 1;
- private final static int MSG_STOP = 2;
- private final static int MSG_CANCEL = 3;
- private final static int MSG_CHANGE_LISTENER = 4;
+ private static final int MSG_START = 1;
+ private static final int MSG_STOP = 2;
+ private static final int MSG_CANCEL = 3;
+ private static final int MSG_CHANGE_LISTENER = 4;
+ private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;
/** The actual RecognitionService endpoint */
private IRecognitionService mService;
@@ -134,6 +139,7 @@ public class SpeechRecognizer {
/** Handler that will execute the main tasks */
private Handler mHandler = new Handler(Looper.getMainLooper()) {
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -149,10 +155,19 @@ public class SpeechRecognizer {
case MSG_CHANGE_LISTENER:
handleChangeListener((RecognitionListener) msg.obj);
break;
+ case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
+ handleSetTemporaryComponent((ComponentName) msg.obj);
+ break;
}
}
};
+ /**
+ * Temporary queue, saving the messages until the connection will be established, afterwards,
+ * only mHandler will receive the messages
+ */
+ private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>();
+
/** The Listener that will receive all the callbacks */
private final InternalListener mListener = new InternalListener();
@@ -287,11 +302,9 @@ public class SpeechRecognizer {
if (mService == null) {
// First time connection: first establish a connection, then dispatch #startListening.
- connectToSystemService(
- () -> putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)));
- } else {
- putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
+ connectToSystemService();
}
+ putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
}
/**
@@ -336,6 +349,22 @@ public class SpeechRecognizer {
putMessage(Message.obtain(mHandler, MSG_CANCEL));
}
+ /**
+ * Sets a temporary component to power on-device speech recognizer.
+ *
+ * <p>This is only expected to be called in tests, system would reject calls from client apps.
+ *
+ * @param componentName name of the component to set temporary replace speech recognizer. {@code
+ * null} value resets the recognizer to default.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setTemporaryOnDeviceRecognizer(@Nullable ComponentName componentName) {
+ mHandler.sendMessage(
+ Message.obtain(mHandler, MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT, componentName));
+ }
+
private static void checkIsCalledFromMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new RuntimeException(
@@ -344,7 +373,11 @@ public class SpeechRecognizer {
}
private void putMessage(Message msg) {
- mHandler.sendMessage(msg);
+ if (mService == null) {
+ mPendingTasks.offer(msg);
+ } else {
+ mHandler.sendMessage(msg);
+ }
}
/** sends the actual message to the service */
@@ -395,6 +428,22 @@ public class SpeechRecognizer {
}
}
+ private void handleSetTemporaryComponent(ComponentName componentName) {
+ if (DBG) {
+ Log.d(TAG, "handleSetTemporaryComponent, componentName=" + componentName);
+ }
+
+ if (!maybeInitializeManagerService()) {
+ return;
+ }
+
+ try {
+ mManagerService.setTemporaryComponent(componentName);
+ } catch (final RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private boolean checkOpenConnection() {
if (mService != null) {
return true;
@@ -422,16 +471,13 @@ public class SpeechRecognizer {
}
mService = null;
+ mPendingTasks.clear();
mListener.mInternalListener = null;
}
/** Establishes a connection to system server proxy and initializes the session. */
- private void connectToSystemService(Runnable onSuccess) {
- mManagerService = IRecognitionServiceManager.Stub.asInterface(
- ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
-
- if (mManagerService == null) {
- mListener.onError(ERROR_CLIENT);
+ private void connectToSystemService() {
+ if (!maybeInitializeManagerService()) {
return;
}
@@ -450,13 +496,19 @@ public class SpeechRecognizer {
new IRecognitionServiceManagerCallback.Stub(){
@Override
public void onSuccess(IRecognitionService service) throws RemoteException {
+ if (DBG) {
+ Log.i(TAG, "Connected to speech recognition service");
+ }
mService = service;
- onSuccess.run();
+ while (!mPendingTasks.isEmpty()) {
+ mHandler.sendMessage(mPendingTasks.poll());
+ }
}
@Override
public void onError(int errorCode) throws RemoteException {
- Log.e(TAG, "Bind to system recognition service failed");
+ Log.e(TAG, "Bind to system recognition service failed with error "
+ + errorCode);
mListener.onError(errorCode);
}
});
@@ -465,6 +517,21 @@ public class SpeechRecognizer {
}
}
+ private boolean maybeInitializeManagerService() {
+ if (mManagerService != null) {
+ return true;
+ }
+
+ mManagerService = IRecognitionServiceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
+
+ if (mManagerService == null && mListener != null) {
+ mListener.onError(ERROR_CLIENT);
+ return false;
+ }
+ return true;
+ }
+
/**
* Returns the component name to be used for establishing a connection, based on the parameters
* used during initialization.
@@ -505,15 +572,15 @@ public class SpeechRecognizer {
private static class InternalListener extends IRecognitionListener.Stub {
private RecognitionListener mInternalListener;
- private final static int MSG_BEGINNING_OF_SPEECH = 1;
- private final static int MSG_BUFFER_RECEIVED = 2;
- private final static int MSG_END_OF_SPEECH = 3;
- private final static int MSG_ERROR = 4;
- private final static int MSG_READY_FOR_SPEECH = 5;
- private final static int MSG_RESULTS = 6;
- private final static int MSG_PARTIAL_RESULTS = 7;
- private final static int MSG_RMS_CHANGED = 8;
- private final static int MSG_ON_EVENT = 9;
+ private static final int MSG_BEGINNING_OF_SPEECH = 1;
+ private static final int MSG_BUFFER_RECEIVED = 2;
+ private static final int MSG_END_OF_SPEECH = 3;
+ private static final int MSG_ERROR = 4;
+ private static final int MSG_READY_FOR_SPEECH = 5;
+ private static final int MSG_RESULTS = 6;
+ private static final int MSG_PARTIAL_RESULTS = 7;
+ private static final int MSG_RMS_CHANGED = 8;
+ private static final int MSG_ON_EVENT = 9;
private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
@Override
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index bbe887f500a9..d47ae2783336 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -20,7 +20,6 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
@@ -1262,6 +1261,8 @@ public class PhoneStateListener {
// default implementation empty
}
+
+
/**
* The callback methods need to be called on the handler thread where
* this object was created. If the binder did that for us it'd be nice.
@@ -1577,7 +1578,12 @@ public class PhoneStateListener {
// default implementation empty
}
- public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+ public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
+ // default implementation empty
+ }
+
+ public void onLinkCapacityEstimateChanged(
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
// default implementation empty
}
}
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 2cadda25a9d3..d0000005c237 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -546,9 +546,6 @@ public class TelephonyCallback {
/**
* Event for changes to allowed network list based on all active subscriptions.
*
- * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
* @hide
* @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
*/
@@ -568,6 +565,21 @@ public class TelephonyCallback {
@RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36;
+
+ /**
+ * Event for changes to the link capacity estimate (LCE)
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ *
+ * @see LinkCapacityEstimateChangedListener#onLinkCapacityEstimateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37;
+
+
/**
* @hide
*/
@@ -607,7 +619,8 @@ public class TelephonyCallback {
EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
EVENT_DATA_ENABLED_CHANGED,
EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED,
- EVENT_LEGACY_CALL_STATE_CHANGED
+ EVENT_LEGACY_CALL_STATE_CHANGED,
+ EVENT_LINK_CAPACITY_ESTIMATE_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface TelephonyEvent {
@@ -1265,30 +1278,34 @@ public class TelephonyCallback {
public interface AllowedNetworkTypesListener {
/**
* Callback invoked when the current allowed network type list has changed on the
- * registered subscription.
+ * registered subscription for a specified reason.
* Note, the registered subscription is associated with {@link TelephonyManager} object
- * on which
- * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+ * on which {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
* was called.
* If this TelephonyManager object was created with
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* given subscription ID. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
- * @param allowedNetworkTypesList Map associating all allowed network type reasons
- * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
- * network type values.
+ * @param reason an allowed network type reasons.
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+ *
+ * @param allowedNetworkType an allowed network type bitmask value. (for example,
+ * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}|
+ * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}})
+ *
* For example:
- * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
+ * If the latest allowed network type is changed by user, then the system
+ * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and
+ * long type value}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+ void onAllowedNetworkTypesChanged(
+ @TelephonyManager.AllowedNetworkTypesReason int reason,
+ @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
}
/**
@@ -1336,10 +1353,7 @@ public class TelephonyCallback {
/**
* Interface for current physical channel configuration listener.
- *
- * @hide
*/
- @SystemApi
public interface PhysicalChannelConfigListener {
/**
* Callback invoked when the current physical channel configuration has changed
@@ -1369,6 +1383,25 @@ public class TelephonyCallback {
@TelephonyManager.DataEnabledReason int reason);
}
+ /**
+ * Interface for link capacity estimate changed listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface LinkCapacityEstimateChangedListener {
+ /**
+ * Callback invoked when the link capacity estimate (LCE) changes
+ *
+ * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate}
+ * The list size is at least 1.
+ * In case of a dual connected network, the list size could be 2.
+ * Use {@link LinkCapacityEstimate#getType()} to get the type of each element.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ void onLinkCapacityEstimateChanged(
+ @NonNull List<LinkCapacityEstimate> linkCapacityEstimateList);
+ }
/**
* The callback methods need to be called on the handler thread where
@@ -1707,14 +1740,26 @@ public class TelephonyCallback {
enabled, reason)));
}
- public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+ public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
AllowedNetworkTypesListener listener =
(AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
if (listener == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(
- () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+ () -> listener.onAllowedNetworkTypesChanged(reason,
+ allowedNetworkType)));
+ }
+
+ public void onLinkCapacityEstimateChanged(
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ LinkCapacityEstimateChangedListener listener =
+ (LinkCapacityEstimateChangedListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onLinkCapacityEstimateChanged(
+ linkCapacityEstimateList)));
}
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9cda4ae79335..1ec12fe12b36 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -825,24 +825,40 @@ public class TelephonyRegistryManager {
}
/**
- * Notify emergency number list changed on certain subscription.
- *
- * @param slotIndex for which emergency number list changed. Can be derived from subId except
- * when subId is invalid.
- * @param subId for which emergency number list changed.
+ * Notify the allowed network types has changed for a specific subscription and the specific
+ * reason.
+ * @param slotIndex for which allowed network types changed.
+ * @param subId for which allowed network types changed.
+ * @param reason an allowed network type reasons.
+ * @param allowedNetworkType an allowed network type bitmask value.
*/
public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
- Map<Integer, Long> allowedNetworkTypeList) {
+ int reason, long allowedNetworkType) {
try {
- sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList);
+ sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason,
+ allowedNetworkType);
} catch (RemoteException ex) {
// system process is dead
}
}
+ /**
+ * Notify that the link capacity estimate has changed.
+ * @param slotIndex for the phone object that gets the updated link capacity estimate
+ * @param subId for subscription that gets the updated link capacity estimate
+ * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate}
+ */
+ public void notifyLinkCapacityEstimateChanged(int slotIndex, int subId,
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ try {
+ sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
public @NonNull Set<Integer> getEventsFromCallback(
@NonNull TelephonyCallback telephonyCallback) {
-
Set<Integer> eventList = new ArraySet<>();
if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) {
@@ -974,6 +990,10 @@ public class TelephonyRegistryManager {
eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
}
+ if (telephonyCallback instanceof TelephonyCallback.LinkCapacityEstimateChangedListener) {
+ eventList.add(TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
+ }
+
return eventList;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 4d321079416b..919c6e50e3a4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -39,7 +39,6 @@ public class FeatureFlagUtils {
public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
- public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
/** @hide */
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
@@ -59,7 +58,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
- DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "true");
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 3880131324fc..f61ab2985163 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,14 +16,26 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Formatter;
+import java.util.Locale;
+
/**
* @hide
*/
public final class Slog {
+ @GuardedBy("sMessageBuilder")
+ private static final StringBuilder sMessageBuilder = new StringBuilder();
+
+ @GuardedBy("sMessageBuilder")
+ private static final Formatter sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH);
+
private Slog() {
}
@@ -37,6 +49,15 @@ public final class Slog {
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.VERBOSE} message.
+ */
+ public static void v(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.VERBOSE)) return;
+
+ v(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int d(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
@@ -48,6 +69,15 @@ public final class Slog {
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.DEBUG} message.
+ */
+ public static void d(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.DEBUG)) return;
+
+ d(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int i(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
@@ -58,6 +88,15 @@ public final class Slog {
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.INFO} message.
+ */
+ public static void i(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.INFO)) return;
+
+ i(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int w(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
@@ -73,6 +112,24 @@ public final class Slog {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.WARN} message.
+ */
+ public static void w(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.WARN)) return;
+
+ w(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@link Log.WARN} message with an exception
+ */
+ public static void w(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.WARN)) return;
+
+ w(tag, getMessage(format, args), exception);
+ }
+
@UnsupportedAppUsage
public static int e(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
@@ -85,6 +142,24 @@ public final class Slog {
}
/**
+ * Logs a {@link Log.ERROR} message.
+ */
+ public static void e(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+ e(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@link Log.ERROR} message with an exception
+ */
+ public static void e(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+ e(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
* will always be handled asynchronously. Primarily for use by coding running within
* the system process.
@@ -95,6 +170,21 @@ public final class Slog {
}
/**
+ * Logs a {@code wtf} message.
+ */
+ public static void wtf(String tag, String format, @Nullable Object... args) {
+ wtf(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@code wtf} message with an exception.
+ */
+ public static void wtf(String tag, Exception exception, String format,
+ @Nullable Object... args) {
+ wtf(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Like {@link #wtf(String, String)}, but does not output anything to the log.
*/
public static void wtfQuiet(String tag, String msg) {
@@ -134,5 +224,13 @@ public final class Slog {
public static int println(int priority, String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
}
-}
+ private static String getMessage(String format, @Nullable Object... args) {
+ synchronized (sMessageBuilder) {
+ sFormatter.format(format, args);
+ String message = sMessageBuilder.toString();
+ sMessageBuilder.setLength(0);
+ return message;
+ }
+ }
+}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index c97c995641d1..7e6175c03d35 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -200,10 +200,9 @@ public final class ApkSigningBlockUtils {
// physical memory.
DataSource beforeApkSigningBlock =
- new MemoryMappedFileDataSource(apkFileDescriptor, 0,
- signatureInfo.apkSigningBlockOffset);
+ DataSource.create(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset);
DataSource centralDir =
- new MemoryMappedFileDataSource(
+ DataSource.create(
apkFileDescriptor, signatureInfo.centralDirOffset,
signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java
index 82f3800aa6d3..dd6389d012bc 100644
--- a/core/java/android/util/apk/DataSource.java
+++ b/core/java/android/util/apk/DataSource.java
@@ -16,6 +16,10 @@
package android.util.apk;
+import android.annotation.NonNull;
+import android.os.incremental.IncrementalManager;
+
+import java.io.FileDescriptor;
import java.io.IOException;
import java.security.DigestException;
@@ -35,4 +39,22 @@ interface DataSource {
*/
void feedIntoDataDigester(DataDigester md, long offset, int size)
throws IOException, DigestException;
+
+ /**
+ * Creates a DataSource that can handle the passed fd in the most efficient and safe manner.
+ * @param fd file descriptor to read from
+ * @param pos starting offset
+ * @param size size of the region
+ * @return created DataSource object
+ */
+ static @NonNull DataSource create(@NonNull FileDescriptor fd, long pos, long size) {
+ if (IncrementalManager.isIncrementalFileFd(fd)) {
+ // IncFS-based files may have missing pages, and reading those via mmap() results
+ // in a SIGBUS signal. Java doesn't have a good way of catching it, ending up killing
+ // the process by default. Going back to read() is the safest option for these files.
+ return new ReadFileDataSource(fd, pos, size);
+ } else {
+ return new MemoryMappedFileDataSource(fd, pos, size);
+ }
+ }
}
diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java
index 8d2b1e328862..69a526d09ad9 100644
--- a/core/java/android/util/apk/MemoryMappedFileDataSource.java
+++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java
@@ -40,6 +40,7 @@ class MemoryMappedFileDataSource implements DataSource {
/**
* Constructs a new {@code MemoryMappedFileDataSource} for the specified region of the file.
*
+ * @param fd file descriptor to read from.
* @param position start position of the region in the file.
* @param size size (in bytes) of the region.
*/
diff --git a/core/java/android/util/apk/ReadFileDataSource.java b/core/java/android/util/apk/ReadFileDataSource.java
new file mode 100644
index 000000000000..d0e1140c0eb4
--- /dev/null
+++ b/core/java/android/util/apk/ReadFileDataSource.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.apk;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+
+/**
+ * {@link DataSource} which provides data from a file descriptor by reading the sections
+ * of the file via raw read() syscall. This is slower than memory-mapping but safer.
+ */
+class ReadFileDataSource implements DataSource {
+ private final FileDescriptor mFd;
+ private final long mFilePosition;
+ private final long mSize;
+
+ private static final int CHUNK_SIZE = 1024 * 1024;
+
+ /**
+ * Constructs a new {@code ReadFileDataSource} for the specified region of the file.
+ *
+ * @param fd file descriptor to read from.
+ * @param position start position of the region in the file.
+ * @param size size (in bytes) of the region.
+ */
+ ReadFileDataSource(FileDescriptor fd, long position, long size) {
+ mFd = fd;
+ mFilePosition = position;
+ mSize = size;
+ }
+
+ @Override
+ public long size() {
+ return mSize;
+ }
+
+ @Override
+ public void feedIntoDataDigester(DataDigester md, long offset, int size)
+ throws IOException, DigestException {
+ try {
+ final byte[] buffer = new byte[Math.min(size, CHUNK_SIZE)];
+ final long start = mFilePosition + offset;
+ final long end = start + size;
+ for (long pos = start, curSize = Math.min(size, CHUNK_SIZE);
+ pos < end; curSize = Math.min(end - pos, CHUNK_SIZE)) {
+ final int readSize = Os.pread(mFd, buffer, 0, (int) curSize, pos);
+ md.consume(ByteBuffer.wrap(buffer, 0, readSize));
+ pos += readSize;
+ }
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
+ }
+}
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
index 8544e82e04e0..4598b4ffe4f6 100644
--- a/core/java/android/util/apk/TEST_MAPPING
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -1,6 +1,17 @@
{
"presubmit": [
{
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksCoreTests",
"options": [
{
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index 4596c6e8f83d..b0a5992230bd 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -294,7 +294,7 @@ public abstract class VerityBuilder {
// 1. Digest the whole file by chunks.
consumeByChunk(digester,
- new MemoryMappedFileDataSource(file.getFD(), 0, file.length()),
+ DataSource.create(file.getFD(), 0, file.length()),
MMAP_REGION_SIZE_BYTES);
// 2. Pad 0s up to the nearest 4096-byte block before hashing.
@@ -315,7 +315,7 @@ public abstract class VerityBuilder {
// 1. Digest from the beginning of the file, until APK Signing Block is reached.
consumeByChunk(digester,
- new MemoryMappedFileDataSource(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
+ DataSource.create(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
MMAP_REGION_SIZE_BYTES);
// 2. Skip APK Signing Block and continue digesting, until the Central Directory offset
@@ -323,7 +323,7 @@ public abstract class VerityBuilder {
long eocdCdOffsetFieldPosition =
signatureInfo.eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET;
consumeByChunk(digester,
- new MemoryMappedFileDataSource(apk.getFD(), signatureInfo.centralDirOffset,
+ DataSource.create(apk.getFD(), signatureInfo.centralDirOffset,
eocdCdOffsetFieldPosition - signatureInfo.centralDirOffset),
MMAP_REGION_SIZE_BYTES);
@@ -338,7 +338,7 @@ public abstract class VerityBuilder {
long offsetAfterEocdCdOffsetField =
eocdCdOffsetFieldPosition + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
consumeByChunk(digester,
- new MemoryMappedFileDataSource(apk.getFD(), offsetAfterEocdCdOffsetField,
+ DataSource.create(apk.getFD(), offsetAfterEocdCdOffsetField,
apk.length() - offsetAfterEocdCdOffsetField),
MMAP_REGION_SIZE_BYTES);
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
index 49ff237403b2..b28cfb87e28d 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -23,7 +23,6 @@ import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
@@ -104,12 +103,6 @@ public abstract class ImeTracing {
public abstract void addToBuffer(ProtoOutputStream proto, int source);
/**
- * @param shell The shell command to process
- * @return {@code 0} if the command was successfully processed, {@code -1} otherwise
- */
- public abstract int onShellCommand(ShellCommand shell);
-
- /**
* Starts a proto dump of the client side information.
*
* @param where Place where the trace was triggered.
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
index 2c2763988d14..35a81b7aeea5 100644
--- a/core/java/android/util/imetracing/ImeTracingClientImpl.java
+++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
@@ -45,11 +44,6 @@ class ImeTracingClientImpl extends ImeTracing {
}
@Override
- public int onShellCommand(ShellCommand shell) {
- return -1;
- }
-
- @Override
public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
ProtoOutputStream icProto) {
if (!isEnabled() || !isAvailable()) {
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
index e793c280afbc..77f017a4654a 100644
--- a/core/java/android/util/imetracing/ImeTracingServerImpl.java
+++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
@@ -106,32 +105,6 @@ class ImeTracingServerImpl extends ImeTracing {
}
}
- /**
- * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>"
- *
- * @param shell The shell command to process
- * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise
- */
- @Override
- public int onShellCommand(ShellCommand shell) {
- PrintWriter pw = shell.getOutPrintWriter();
- String cmd = shell.getNextArgRequired();
- switch (cmd) {
- case "start":
- startTrace(pw);
- return 0;
- case "stop":
- stopTrace(pw);
- return 0;
- default:
- pw.println("Unknown command: " + cmd);
- pw.println("Input method trace options:");
- pw.println(" start: Start tracing");
- pw.println(" stop: Stop tracing");
- return -1;
- }
- }
-
@Override
public void triggerClientDump(String where, InputMethodManager immInstance,
ProtoOutputStream icProto) {
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 9df213b2092f..8c771baaea37 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -38,9 +38,30 @@ public final class AngleMeasurement implements Parcelable {
private final double mErrorRadians;
private final double mConfidenceLevel;
- private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+ /**
+ * Constructs a new {@link AngleMeasurement} object
+ *
+ * @param radians the angle in radians
+ * @param errorRadians the error of the angle measurement in radians
+ * @param confidenceLevel confidence level of the angle measurement
+ *
+ * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
+ * allowed range
+ */
+ public AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+ if (radians < -Math.PI || radians > Math.PI) {
+ throw new IllegalArgumentException("Invalid radians: " + radians);
+ }
mRadians = radians;
+
+ if (errorRadians < 0.0 || errorRadians > Math.PI) {
+ throw new IllegalArgumentException("Invalid error radians: " + errorRadians);
+ }
mErrorRadians = errorRadians;
+
+ if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+ throw new IllegalArgumentException("Invalid confidence level: " + confidenceLevel);
+ }
mConfidenceLevel = confidenceLevel;
}
@@ -122,11 +143,7 @@ public final class AngleMeasurement implements Parcelable {
new Creator<AngleMeasurement>() {
@Override
public AngleMeasurement createFromParcel(Parcel in) {
- Builder builder = new Builder();
- builder.setRadians(in.readDouble());
- builder.setErrorRadians(in.readDouble());
- builder.setConfidenceLevel(in.readDouble());
- return builder.build();
+ return new AngleMeasurement(in.readDouble(), in.readDouble(), in.readDouble());
}
@Override
@@ -134,82 +151,4 @@ public final class AngleMeasurement implements Parcelable {
return new AngleMeasurement[size];
}
};
-
- /**
- * Builder class for {@link AngleMeasurement}.
- */
- public static final class Builder {
- private double mRadians = Double.NaN;
- private double mErrorRadians = Double.NaN;
- private double mConfidenceLevel = Double.NaN;
-
- /**
- * Set the angle in radians
- *
- * @param radians angle in radians
- * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
- */
- @NonNull
- public Builder setRadians(double radians) {
- if (radians < -Math.PI || radians > Math.PI) {
- throw new IllegalArgumentException("Invalid radians: " + radians);
- }
- mRadians = radians;
- return this;
- }
-
- /**
- * Set the angle error in radians
- *
- * @param errorRadians error of the angle in radians
- * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
- */
- @NonNull
- public Builder setErrorRadians(double errorRadians) {
- if (errorRadians < 0.0 || errorRadians > Math.PI) {
- throw new IllegalArgumentException(
- "Invalid error radians: " + errorRadians);
- }
- mErrorRadians = errorRadians;
- return this;
- }
-
- /**
- * Set the angle confidence level
- *
- * @param confidenceLevel level of confidence of the angle measurement
- * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
- */
- @NonNull
- public Builder setConfidenceLevel(double confidenceLevel) {
- if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
- throw new IllegalArgumentException(
- "Invalid confidence level: " + confidenceLevel);
- }
- mConfidenceLevel = confidenceLevel;
- return this;
- }
-
- /**
- * Build the {@link AngleMeasurement} object
- *
- * @throws IllegalStateException if angle, error, or confidence values are missing
- */
- @NonNull
- public AngleMeasurement build() {
- if (Double.isNaN(mRadians)) {
- throw new IllegalStateException("Angle is not set");
- }
-
- if (Double.isNaN(mErrorRadians)) {
- throw new IllegalStateException("Angle error is not set");
- }
-
- if (Double.isNaN(mConfidenceLevel)) {
- throw new IllegalStateException("Angle confidence level is not set");
- }
-
- return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel);
- }
- }
}
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 3d8626b98bed..db04ad16c191 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -116,9 +116,8 @@ public final class AngleOfArrivalMeasurement implements Parcelable {
new Creator<AngleOfArrivalMeasurement>() {
@Override
public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
- Builder builder = new Builder();
-
- builder.setAzimuth(in.readParcelable(AngleMeasurement.class.getClassLoader()));
+ Builder builder =
+ new Builder(in.readParcelable(AngleMeasurement.class.getClassLoader()));
builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
@@ -135,18 +134,16 @@ public final class AngleOfArrivalMeasurement implements Parcelable {
* Builder class for {@link AngleOfArrivalMeasurement}.
*/
public static final class Builder {
- private AngleMeasurement mAzimuthAngleMeasurement = null;
+ private final AngleMeasurement mAzimuthAngleMeasurement;
private AngleMeasurement mAltitudeAngleMeasurement = null;
/**
- * Set the azimuth angle
+ * Constructs an {@link AngleOfArrivalMeasurement} object
*
- * @param azimuthAngle azimuth angle
+ * @param azimuthAngle the azimuth angle of the measurement
*/
- @NonNull
- public Builder setAzimuth(@NonNull AngleMeasurement azimuthAngle) {
+ public Builder(@NonNull AngleMeasurement azimuthAngle) {
mAzimuthAngleMeasurement = azimuthAngle;
- return this;
}
/**
@@ -162,15 +159,9 @@ public final class AngleOfArrivalMeasurement implements Parcelable {
/**
* Build the {@link AngleOfArrivalMeasurement} object
- *
- * @throws IllegalStateException if the required azimuth angle is not provided
*/
@NonNull
public AngleOfArrivalMeasurement build() {
- if (mAzimuthAngleMeasurement == null) {
- throw new IllegalStateException("Azimuth angle measurement is not set");
- }
-
return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
mAltitudeAngleMeasurement);
}
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 468a69c7bddb..4036892fb9e7 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -62,9 +62,6 @@ interface IUwbAdapter {
/**
* Request to open a new ranging session
*
- * This function must return before calling any functions in
- * IUwbAdapterCallbacks.
- *
* This function does not start the ranging session, but all necessary
* components must be initialized and ready to start a new ranging
* session prior to calling IUwbAdapterCallback#onRangingOpened.
@@ -77,12 +74,16 @@ interface IUwbAdapter {
* RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
* if the ranging session fails to be opened.
*
+ * If the provided sessionHandle is already open for the calling client, then
+ * #onRangingOpenFailed must be called and the new session must not be opened.
+ *
+ * @param sessionHandle the session handle to open ranging for
* @param rangingCallbacks the callbacks used to deliver ranging information
* @param parameters the configuration to use for ranging
- * @return a SessionHandle used to identify this ranging request
*/
- SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks,
- in PersistableBundle parameters);
+ void openRanging(in SessionHandle sessionHandle,
+ in IUwbRangingCallbacks rangingCallbacks,
+ in PersistableBundle parameters);
/**
* Request to start ranging
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index c0d818774ba0..85f2c1ccc180 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -17,6 +17,7 @@
package android.uwb;
import android.annotation.NonNull;
+import android.os.CancellationSignal;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Log;
@@ -32,6 +33,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
private final IUwbAdapter mAdapter;
private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
+ private int mNextSessionId = 1;
public RangingManager(IUwbAdapter adapter) {
mAdapter = adapter;
@@ -44,29 +46,26 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
* @param executor {@link Executor} to run callbacks
* @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
* that is being opened.
- * @return a new {@link RangingSession}
+ * @return a {@link CancellationSignal} that may be used to cancel the opening of the
+ * {@link RangingSession}.
*/
- public RangingSession openSession(@NonNull PersistableBundle params, @NonNull Executor executor,
+ public CancellationSignal openSession(@NonNull PersistableBundle params,
+ @NonNull Executor executor,
@NonNull RangingSession.Callback callbacks) {
- SessionHandle sessionHandle;
- try {
- sessionHandle = mAdapter.openRanging(this, params);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
synchronized (this) {
- if (hasSession(sessionHandle)) {
- Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle");
- executor.execute(() -> callbacks.onClosed(
- RangingSession.Callback.REASON_GENERIC_ERROR,
- new PersistableBundle()));
- }
-
+ SessionHandle sessionHandle = new SessionHandle(mNextSessionId++);
RangingSession session =
new RangingSession(executor, callbacks, mAdapter, sessionHandle);
mRangingSessionTable.put(sessionHandle, session);
- return session;
+ try {
+ mAdapter.openRanging(sessionHandle, this, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ cancellationSignal.setOnCancelListener(() -> session.close());
+ return cancellationSignal;
}
}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 63a6d058f358..844bbbe7970b 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -25,6 +25,7 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -228,14 +229,14 @@ public final class UwbManager {
* @param callbacks {@link RangingSession.Callback} to associate with the
* {@link RangingSession} that is being opened.
*
- * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
+ * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a
* {@link RangingSession} that has been requested through {@link #openRangingSession}
* but has not yet been made available by
* {@link RangingSession.Callback#onOpened(RangingSession)}.
*/
@NonNull
@RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
+ public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters,
@NonNull @CallbackExecutor Executor executor,
@NonNull RangingSession.Callback callbacks) {
return mRangingManager.openSession(parameters, executor, callbacks);
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index 5a1b850133cb..55fc4f41f5eb 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -16,13 +16,19 @@
package android.view;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -42,7 +48,7 @@ public final class CrossWindowBlurListeners {
private static final Object sLock = new Object();
private final BlurEnabledListenerInternal mListenerInternal = new BlurEnabledListenerInternal();
- private final ArraySet<Consumer<Boolean>> mListeners = new ArraySet();
+ private final ArrayMap<Consumer<Boolean>, Executor> mListeners = new ArrayMap();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private boolean mInternalListenerAttached = false;
private boolean mCrossWindowBlurEnabled;
@@ -74,20 +80,22 @@ public final class CrossWindowBlurListeners {
}
}
- void addListener(Consumer<Boolean> listener) {
- if (listener == null) return;
+ void addListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(executor, "executor cannot be null");
synchronized (sLock) {
attachInternalListenerIfNeededLocked();
- mListeners.add(listener);
- notifyListenerOnMain(listener, mCrossWindowBlurEnabled);
+ mListeners.put(listener, executor);
+ notifyListener(listener, executor, mCrossWindowBlurEnabled);
}
}
void removeListener(Consumer<Boolean> listener) {
- if (listener == null) return;
+ Preconditions.checkNotNull(listener, "listener cannot be null");
synchronized (sLock) {
mListeners.remove(listener);
@@ -116,10 +124,8 @@ public final class CrossWindowBlurListeners {
}
}
- private void notifyListenerOnMain(Consumer<Boolean> listener, boolean enabled) {
- mMainHandler.post(() -> {
- listener.accept(enabled);
- });
+ private void notifyListener(Consumer<Boolean> listener, Executor executor, boolean enabled) {
+ executor.execute(() -> listener.accept(enabled));
}
private final class BlurEnabledListenerInternal extends ICrossWindowBlurEnabledListener.Stub {
@@ -128,8 +134,13 @@ public final class CrossWindowBlurListeners {
synchronized (sLock) {
mCrossWindowBlurEnabled = enabled;
- for (int i = 0; i < mListeners.size(); i++) {
- notifyListenerOnMain(mListeners.valueAt(i), enabled);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mListeners.size(); i++) {
+ notifyListener(mListeners.keyAt(i), mListeners.valueAt(i), enabled);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d138b4b41450..d484f4d47e99 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -24,8 +24,11 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.app.KeyguardManager;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -44,11 +47,14 @@ import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
+import com.android.internal.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
/**
* Provides information about the size and density of a logical display.
@@ -78,6 +84,7 @@ public final class Display {
private static final String TAG = "Display";
private static final boolean DEBUG = false;
+ private final Object mLock = new Object();
private final DisplayManagerGlobal mGlobal;
private final int mDisplayId;
private final int mFlags;
@@ -112,6 +119,12 @@ public final class Display {
private boolean mMayAdjustByFixedRotation;
/**
+ * Cache if the application is the recents component.
+ * TODO(b/179308296) Remove once Launcher addresses issue
+ */
+ private Optional<Boolean> mIsRecentsComponent = Optional.empty();
+
+ /**
* The default Display id, which is the id of the primary display assuming there is one.
*/
public static final int DEFAULT_DISPLAY = 0;
@@ -557,7 +570,7 @@ public final class Display {
* @return True if the display is still valid.
*/
public boolean isValid() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mIsValid;
}
@@ -572,7 +585,7 @@ public final class Display {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public boolean getDisplayInfo(DisplayInfo outDisplayInfo) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
outDisplayInfo.copyFrom(mDisplayInfo);
return mIsValid;
@@ -589,7 +602,7 @@ public final class Display {
* @hide
*/
public int getLayerStack() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.layerStack;
}
@@ -636,7 +649,7 @@ public final class Display {
* @hide
*/
public DisplayAddress getAddress() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.address;
}
@@ -678,9 +691,9 @@ public final class Display {
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustements)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
+ final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustments)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
}
}
@@ -696,7 +709,7 @@ public final class Display {
* @return The display's name.
*/
public String getName() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.name;
}
@@ -709,7 +722,7 @@ public final class Display {
* @hide
*/
public float getBrightnessDefault() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.brightnessDefault;
}
@@ -748,7 +761,7 @@ public final class Display {
*/
@Deprecated
public void getSize(Point outSize) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
outSize.x = mTempMetrics.widthPixels;
@@ -765,7 +778,7 @@ public final class Display {
*/
@Deprecated
public void getRectSize(Rect outSize) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
outSize.set(0, 0, mTempMetrics.widthPixels, mTempMetrics.heightPixels);
@@ -803,7 +816,7 @@ public final class Display {
* for example, screen decorations like the status bar are being hidden.
*/
public void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
outSmallestSize.x = mDisplayInfo.smallestNominalAppWidth;
outSmallestSize.y = mDisplayInfo.smallestNominalAppHeight;
@@ -819,7 +832,7 @@ public final class Display {
*/
@UnsupportedAppUsage
public int getMaximumSizeDimension() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return Math.max(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
}
@@ -830,7 +843,7 @@ public final class Display {
*/
@Deprecated
public int getWidth() {
- synchronized (this) {
+ synchronized (mLock) {
updateCachedAppSizeIfNeededLocked();
return mCachedAppWidthCompat;
}
@@ -841,7 +854,7 @@ public final class Display {
*/
@Deprecated
public int getHeight() {
- synchronized (this) {
+ synchronized (mLock) {
updateCachedAppSizeIfNeededLocked();
return mCachedAppHeightCompat;
}
@@ -866,7 +879,7 @@ public final class Display {
*/
@Surface.Rotation
public int getRotation() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mMayAdjustByFixedRotation
? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
@@ -892,7 +905,7 @@ public final class Display {
*/
@Nullable
public DisplayCutout getCutout() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mMayAdjustByFixedRotation
? getDisplayAdjustments().getDisplayCutout(mDisplayInfo.displayCutout)
@@ -910,7 +923,7 @@ public final class Display {
@SuppressLint("VisiblySynchronized")
@Nullable
public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
RoundedCorners roundedCorners;
if (mMayAdjustByFixedRotation) {
@@ -942,7 +955,7 @@ public final class Display {
* Gets the refresh rate of this display in frames per second.
*/
public float getRefreshRate() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.getRefreshRate();
}
@@ -958,7 +971,7 @@ public final class Display {
*/
@Deprecated
public float[] getSupportedRefreshRates() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.getDefaultRefreshRates();
}
@@ -968,7 +981,7 @@ public final class Display {
* Returns the active mode of the display.
*/
public Mode getMode() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.getMode();
}
@@ -978,7 +991,7 @@ public final class Display {
* Gets the supported modes of this display.
*/
public Mode[] getSupportedModes() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
final Display.Mode[] modes = mDisplayInfo.supportedModes;
return Arrays.copyOf(modes, modes.length);
@@ -1004,7 +1017,7 @@ public final class Display {
*/
@SuppressLint("VisiblySynchronized")
public boolean isMinimalPostProcessingSupported() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.minimalPostProcessingSupported;
}
@@ -1024,7 +1037,7 @@ public final class Display {
* @hide
*/
public int getColorMode() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.colorMode;
}
@@ -1051,7 +1064,7 @@ public final class Display {
* @see #isHdr()
*/
public HdrCapabilities getHdrCapabilities() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.hdrCapabilities;
}
@@ -1064,7 +1077,7 @@ public final class Display {
* @see HdrCapabilities#getSupportedHdrTypes()
*/
public boolean isHdr() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.isHdr();
}
@@ -1077,7 +1090,7 @@ public final class Display {
* {@link Configuration#isScreenWideColorGamut()}.
*/
public boolean isWideColorGamut() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.isWideColorGamut();
}
@@ -1092,7 +1105,7 @@ public final class Display {
*/
@Nullable
public ColorSpace getPreferredWideGamutColorSpace() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
if (mDisplayInfo.isWideColorGamut()) {
return mGlobal.getPreferredWideGamutColorSpace();
@@ -1106,7 +1119,7 @@ public final class Display {
* @hide
*/
public int[] getSupportedColorModes() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
int[] colorModes = mDisplayInfo.supportedColorModes;
return Arrays.copyOf(colorModes, colorModes.length);
@@ -1123,7 +1136,7 @@ public final class Display {
@NonNull
@TestApi
public @ColorMode ColorSpace[] getSupportedWideColorGamut() {
- synchronized (this) {
+ synchronized (mLock) {
final ColorSpace[] defaultColorSpaces = new ColorSpace[0];
updateDisplayInfoLocked();
if (!isWideColorGamut()) {
@@ -1157,7 +1170,7 @@ public final class Display {
* A/V synchronization.
*/
public long getAppVsyncOffsetNanos() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.appVsyncOffsetNanos;
}
@@ -1175,7 +1188,7 @@ public final class Display {
* ({@link System#nanoTime}).
*/
public long getPresentationDeadlineNanos() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mDisplayInfo.presentationDeadlineNanos;
}
@@ -1190,7 +1203,10 @@ public final class Display {
*/
@Nullable
public DeviceProductInfo getDeviceProductInfo() {
- return mDisplayInfo.deviceProductInfo;
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.deviceProductInfo;
+ }
}
/**
@@ -1223,7 +1239,7 @@ public final class Display {
*/
@Deprecated
public void getMetrics(DisplayMetrics outMetrics) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
mDisplayInfo.getAppMetrics(outMetrics, getDisplayAdjustments());
}
@@ -1276,8 +1292,20 @@ public final class Display {
*/
@Deprecated
public void getRealSize(Point outSize) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ final Rect bounds = mResources.getConfiguration()
+ .windowConfiguration.getMaxBounds();
+ outSize.x = bounds.width();
+ outSize.y = bounds.height();
+ if (DEBUG) {
+ Log.d(TAG, "getRealSize determined from max bounds: " + outSize);
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1334,8 +1362,19 @@ public final class Display {
*/
@Deprecated
public void getRealMetrics(DisplayMetrics outMetrics) {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
+ if (shouldReportMaxBounds()) {
+ mDisplayInfo.getMaxBoundsMetrics(outMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
+ mResources.getConfiguration());
+ if (DEBUG) {
+ Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics);
+ }
+ // Skip adjusting by fixed rotation, since if it is necessary, the configuration
+ // should already reflect the expected rotation.
+ return;
+ }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1345,6 +1384,53 @@ public final class Display {
}
/**
+ * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
+ * display dimensions. The max bounds field may be smaller than the logical dimensions
+ * when apps need to be sandboxed.
+ *
+ * Depends upon {@link WindowConfiguration#getMaxBounds()} being set in
+ * {@link com.android.server.wm.ConfigurationContainer#providesMaxBounds()}. In most cases, this
+ * value reflects the size of the current DisplayArea.
+ * @return {@code true} when max bounds should be applied.
+ */
+ private boolean shouldReportMaxBounds() {
+ if (mResources == null) {
+ return false;
+ }
+ final Configuration config = mResources.getConfiguration();
+ // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking
+ // if the caller is the recents component.
+ return config != null && !config.windowConfiguration.getMaxBounds().isEmpty()
+ && !isRecentsComponent();
+ }
+
+ /**
+ * Returns {@code true} when the calling package is the recents component.
+ * TODO(b/179308296) Remove once Launcher addresses issue
+ */
+ boolean isRecentsComponent() {
+ if (mIsRecentsComponent.isPresent()) {
+ return mIsRecentsComponent.get();
+ }
+ if (mResources == null) {
+ return false;
+ }
+ try {
+ String recentsComponent = mResources.getString(R.string.config_recentsComponentName);
+ if (recentsComponent == null) {
+ return false;
+ }
+ String recentsPackage = ComponentName.unflattenFromString(recentsComponent)
+ .getPackageName();
+ mIsRecentsComponent = Optional.of(recentsPackage != null
+ && recentsPackage.equals(ActivityThread.currentPackageName()));
+ return mIsRecentsComponent.get();
+ } catch (Resources.NotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
@@ -1352,7 +1438,7 @@ public final class Display {
* {@link #STATE_UNKNOWN}.
*/
public int getState() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
return mIsValid ? mDisplayInfo.state : STATE_UNKNOWN;
}
@@ -1436,7 +1522,7 @@ public final class Display {
// For debugging purposes
@Override
public String toString() {
- synchronized (this) {
+ synchronized (mLock) {
updateDisplayInfoLocked();
final DisplayAdjustments adjustments = getDisplayAdjustments();
mDisplayInfo.getAppMetrics(mTempMetrics, adjustments);
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 655f42308a1f..9aaf5c066073 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,6 +24,7 @@ import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -615,11 +616,31 @@ public final class DisplayInfo implements Parcelable {
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
+ /**
+ * Populates {@code outMetrics} with details of the logical display. Bounds are limited
+ * by the logical size of the display.
+ *
+ * @param outMetrics the {@link DisplayMetrics} to be populated
+ * @param compatInfo the {@link CompatibilityInfo} to be applied
+ * @param configuration the {@link Configuration}
+ */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
+ /**
+ * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
+ * {@link WindowConfiguration#getMaxBounds()}
+ */
+ public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
+ Configuration configuration) {
+ Rect bounds = configuration.windowConfiguration.getMaxBounds();
+ // Pass in null configuration to ensure width and height are not overridden to app bounds.
+ getMetricsWithSize(outMetrics, compatInfo, /* configuration= */ null,
+ bounds.width(), bounds.height());
+ }
+
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/DragAndDropPermissions.java b/core/java/android/view/DragAndDropPermissions.java
index d47604d6ec41..16204d852a3a 100644
--- a/core/java/android/view/DragAndDropPermissions.java
+++ b/core/java/android/view/DragAndDropPermissions.java
@@ -16,12 +16,15 @@
package android.view;
+import static java.lang.Integer.toHexString;
+
import android.app.Activity;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.view.IDragAndDropPermissions;
@@ -56,9 +59,28 @@ import com.android.internal.view.IDragAndDropPermissions;
*/
public final class DragAndDropPermissions implements Parcelable {
- private final IDragAndDropPermissions mDragAndDropPermissions;
+ private static final String TAG = "DragAndDrop";
+ private static final boolean DEBUG = false;
+
+ /**
+ * Permissions for a drop can be granted in one of two ways:
+ * <ol>
+ * <li>An app can explicitly request permissions using
+ * {@link Activity#requestDragAndDropPermissions(DragEvent)}. In this case permissions are
+ * revoked automatically when then activity is destroyed. See {@link #take(IBinder)}.
+ * <li>The platform can request permissions on behalf of the app (e.g. in
+ * {@link android.widget.Editor}). In this case permissions are revoked automatically when
+ * the app process terminates. See {@link #takeTransient()}.
+ * </ol>
+ *
+ * <p>In order to implement the second case above, we create a static token object here. This
+ * ensures that the token stays alive for the lifetime of the app process, allowing us to
+ * revoke permissions when the app process terminates using {@link IBinder#linkToDeath} in
+ * {@code DragAndDropPermissionsHandler}.
+ */
+ private static IBinder sAppToken;
- private IBinder mTransientToken;
+ private final IDragAndDropPermissions mDragAndDropPermissions;
/**
* Create a new {@link DragAndDropPermissions} object to control the access permissions for
@@ -81,30 +103,51 @@ public final class DragAndDropPermissions implements Parcelable {
}
/**
- * Take the permissions and bind their lifetime to the activity.
+ * Take permissions, binding their lifetime to the activity.
+ *
+ * <p>Note: This API is exposed to apps via
+ * {@link Activity#requestDragAndDropPermissions(DragEvent)}.
+ *
* @param activityToken Binder pointing to an Activity instance to bind the lifetime to.
* @return True if permissions are successfully taken.
+ *
* @hide
*/
public boolean take(IBinder activityToken) {
try {
+ if (DEBUG) {
+ Log.d(TAG, this + ": calling take() with activity-bound token: "
+ + toHexString(activityToken.hashCode()));
+ }
mDragAndDropPermissions.take(activityToken);
} catch (RemoteException e) {
+ Log.w(TAG, this + ": take() failed with a RemoteException", e);
return false;
}
return true;
}
/**
- * Take the permissions. Must call {@link #release} explicitly.
+ * Take permissions transiently. Permissions will be revoked when the app process terminates.
+ *
+ * <p>Note: This API is not exposed to apps.
+ *
* @return True if permissions are successfully taken.
+ *
* @hide
*/
public boolean takeTransient() {
try {
- mTransientToken = new Binder();
- mDragAndDropPermissions.takeTransient(mTransientToken);
+ if (sAppToken == null) {
+ sAppToken = new Binder();
+ }
+ if (DEBUG) {
+ Log.d(TAG, this + ": calling takeTransient() with process-bound token: "
+ + toHexString(sAppToken.hashCode()));
+ }
+ mDragAndDropPermissions.takeTransient(sAppToken);
} catch (RemoteException e) {
+ Log.w(TAG, this + ": takeTransient() failed with a RemoteException", e);
return false;
}
return true;
@@ -116,8 +159,8 @@ public final class DragAndDropPermissions implements Parcelable {
public void release() {
try {
mDragAndDropPermissions.release();
- mTransientToken = null;
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -142,11 +185,9 @@ public final class DragAndDropPermissions implements Parcelable {
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeStrongInterface(mDragAndDropPermissions);
- destination.writeStrongBinder(mTransientToken);
}
private DragAndDropPermissions(Parcel in) {
mDragAndDropPermissions = IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());
- mTransientToken = in.readStrongBinder();
}
}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index afbd2493bad4..10721ad30525 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -43,8 +43,11 @@ interface IRecentsAnimationController {
* accordingly. This should be called before `finish`
* @param taskId for which the leash should be updated
* @param destinationBounds bounds of the final PiP window
+ * @param windowCrop bounds to crop as part of final transform.
+ * @param float9 An array of 9 floats to be used as matrix transform.
*/
- void setFinishTaskBounds(int taskId, in Rect destinationBounds);
+ void setFinishTaskBounds(int taskId, in Rect destinationBounds, in Rect windowCrop,
+ in float[] float9);
/**
* Notifies to the system that the animation into Recents should end, and all leashes associated
@@ -142,7 +145,13 @@ interface IRecentsAnimationController {
*
* The system reparents the leash of navigation bar to the app when the recents animation starts
* and Launcher should call this method to let system restore the navigation bar to its
- * original position when the quick switch gesture is finished.
+ * original position when the quick switch gesture is finished and will run the fade-in
+ * animation If {@param moveHomeToTop} is {@code true}. Otherwise, restore the navigtation bar
+ * without animation.
+ *
+ * @param moveHomeToTop if {@code true}, the home activity should be moved to the top.
+ * Otherwise, the home activity is hidden and the user is returned to the
+ * app.
*/
- void detachNavigationBarFromApp();
+ void detachNavigationBarFromApp(boolean moveHomeToTop);
}
diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl
index 26eaac0a2bf5..9b3561400efc 100644
--- a/core/java/android/view/IScrollCaptureCallbacks.aidl
+++ b/core/java/android/view/IScrollCaptureCallbacks.aidl
@@ -27,13 +27,6 @@ import android.view.Surface;
*/
interface IScrollCaptureCallbacks {
/**
- * Provides the result of WindowManagerService#requestScrollCapture
- *
- * @param response the response which describes the result
- */
- oneway void onScrollCaptureResponse(in ScrollCaptureResponse response);
-
- /**
* Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed
* the request and is ready to begin capturing images.
*/
diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl
index c55e88800393..3a6b69397919 100644
--- a/core/java/android/view/IScrollCaptureConnection.aidl
+++ b/core/java/android/view/IScrollCaptureConnection.aidl
@@ -18,6 +18,7 @@ package android.view;
import android.graphics.Rect;
import android.os.ICancellationSignal;
+import android.view.IScrollCaptureCallbacks;
import android.view.Surface;
@@ -31,11 +32,12 @@ interface IScrollCaptureConnection {
/**
* Informs the target that it has been selected for scroll capture.
*
- * @param surface a return channel for image buffers
+ * @param surface used to shuttle image buffers between processes
+ * @param callbacks a return channel for requests
*
- * @return a cancallation signal which is used cancel the request
+ * @return a cancallation signal which is used cancel the start request
*/
- ICancellationSignal startCapture(in Surface surface);
+ ICancellationSignal startCapture(in Surface surface, IScrollCaptureCallbacks callbacks);
/**
* Request the target capture an image within the provided rectangle.
diff --git a/core/java/android/view/IScrollCaptureResponseListener.aidl b/core/java/android/view/IScrollCaptureResponseListener.aidl
new file mode 100644
index 000000000000..7220f6ca5661
--- /dev/null
+++ b/core/java/android/view/IScrollCaptureResponseListener.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+import android.view.ScrollCaptureResponse;
+import android.view.Surface;
+
+/**
+ * Asynchronous callback channel for the initial response to a scroll capture request.
+ *
+ * {@hide}
+ */
+interface IScrollCaptureResponseListener {
+ /**
+ * Provides the initial response to a scroll capture request.
+ *
+ * @param response the response which describes the result
+ */
+ oneway void onScrollCaptureResponse(in ScrollCaptureResponse response);
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index fb012ebbc3db..8d59ba0b1f76 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -26,7 +26,7 @@ import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.window.ClientWindowFrames;
@@ -134,5 +134,5 @@ oneway interface IWindow {
*
* @param callbacks to receive responses
*/
- void requestScrollCapture(in IScrollCaptureCallbacks callbacks);
+ void requestScrollCapture(in IScrollCaptureResponseListener callbacks);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 54778007c6ff..a42126f18357 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -42,7 +42,7 @@ import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowRotationController;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedTaskListener;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.ISystemGestureExclusionListener;
@@ -744,10 +744,10 @@ interface IWindowManager
* @param behindClient token for a window, used to filter the search to windows behind it, or
* {@code null} to accept a window at any zOrder
* @param taskId specifies the id of a task the result must belong to, or -1 to ignore task ids
- * @param callbacks the object to receive replies
+ * @param listener the object to receive the response
*/
void requestScrollCapture(int displayId, IBinder behindClient, int taskId,
- IScrollCaptureCallbacks callbacks);
+ IScrollCaptureResponseListener listener);
/**
* Holds the WM lock for the specified amount of milliseconds.
@@ -816,4 +816,6 @@ interface IWindowManager
* @param listener the listener to be unregistered
*/
void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
+
+ void setForceCrossWindowBlurDisabled(boolean disable);
}
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 79d8c14aa0df..5e0579d8a672 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -159,6 +159,16 @@ public abstract class InputEventReceiver {
}
/**
+ * Called when a drag event is received, from native code.
+ *
+ * @param isExiting if false, the window associated with this input channel has just received
+ * drag
+ * if true, the window associated with this input channel has just lost drag
+ */
+ public void onDragEvent(boolean isExiting, float x, float y) {
+ }
+
+ /**
* Called when a batched input event is pending.
*
* The batched input event will continue to accumulate additional movement
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 02a97888cffd..aa1acc1217df 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManager;
import android.os.Build;
@@ -25,6 +26,8 @@ import android.text.method.MetaKeyKeyListener;
import android.util.AndroidRuntimeException;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.text.Normalizer;
/**
@@ -297,6 +300,8 @@ public class KeyCharacterMap implements Parcelable {
private static native char nativeGetDisplayLabel(long ptr, int keyCode);
private static native int nativeGetKeyboardType(long ptr);
private static native KeyEvent[] nativeGetEvents(long ptr, char[] chars);
+ private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
+ private static native boolean nativeEquals(long ptr1, long ptr2);
private KeyCharacterMap(Parcel in) {
if (in == null) {
@@ -323,6 +328,18 @@ public class KeyCharacterMap implements Parcelable {
}
/**
+ * Obtain empty key character map
+ * @param deviceId The input device ID
+ * @return The KeyCharacterMap object
+ * @hide
+ */
+ @VisibleForTesting
+ @Nullable
+ public static KeyCharacterMap obtainEmptyMap(int deviceId) {
+ return nativeObtainEmptyKeyCharacterMap(deviceId);
+ }
+
+ /**
* Loads the key character maps for the keyboard with the specified device id.
*
* @param deviceId The device id of the keyboard.
@@ -729,6 +746,18 @@ public class KeyCharacterMap implements Parcelable {
return 0;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof KeyCharacterMap)) {
+ return false;
+ }
+ KeyCharacterMap peer = (KeyCharacterMap) obj;
+ if (mPtr == 0 || peer.mPtr == 0) {
+ return mPtr == peer.mPtr;
+ }
+ return nativeEquals(mPtr, peer.mPtr);
+ }
+
/**
* Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded.
*/
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index cbb86de4785f..31f6f6afdd53 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -78,3 +78,8 @@ per-file SyncRtSurfaceTransactionApplier.java = file:/services/core/java/com/and
per-file ViewRootInsetsControllerHost.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.aidl = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index b1b670f5e0c9..14dcdad8e2b5 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -31,6 +31,7 @@ import static android.view.RemoteAnimationTargetProto.START_BOUNDS;
import static android.view.RemoteAnimationTargetProto.START_LEASH;
import static android.view.RemoteAnimationTargetProto.TASK_ID;
import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import android.annotation.IntDef;
import android.app.PictureInPictureParams;
@@ -195,12 +196,30 @@ public class RemoteAnimationTarget implements Parcelable {
*/
public PictureInPictureParams pictureInPictureParams;
+ /**
+ * The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used
+ * for non-app window.
+ */
+ public final @WindowManager.LayoutParams.WindowType int windowType;
+
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
SurfaceControl startLeash, Rect startBounds,
PictureInPictureParams pictureInPictureParams) {
+ this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
+ position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
+ startBounds, pictureInPictureParams, INVALID_WINDOW_TYPE);
+ }
+
+ public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
+ Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
+ Rect localBounds, Rect screenSpaceBounds,
+ WindowConfiguration windowConfig, boolean isNotInRecents,
+ SurfaceControl startLeash, Rect startBounds,
+ PictureInPictureParams pictureInPictureParams,
+ @WindowManager.LayoutParams.WindowType int windowType) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
@@ -217,6 +236,7 @@ public class RemoteAnimationTarget implements Parcelable {
this.startLeash = startLeash;
this.startBounds = startBounds == null ? null : new Rect(startBounds);
this.pictureInPictureParams = pictureInPictureParams;
+ this.windowType = windowType;
}
public RemoteAnimationTarget(Parcel in) {
@@ -236,6 +256,7 @@ public class RemoteAnimationTarget implements Parcelable {
startLeash = in.readTypedObject(SurfaceControl.CREATOR);
startBounds = in.readTypedObject(Rect.CREATOR);
pictureInPictureParams = in.readTypedObject(PictureInPictureParams.CREATOR);
+ windowType = in.readInt();
}
@Override
@@ -261,6 +282,7 @@ public class RemoteAnimationTarget implements Parcelable {
dest.writeTypedObject(startLeash, 0 /* flags */);
dest.writeTypedObject(startBounds, 0 /* flags */);
dest.writeTypedObject(pictureInPictureParams, 0 /* flags */);
+ dest.writeInt(windowType);
}
public void dump(PrintWriter pw, String prefix) {
@@ -278,6 +300,7 @@ public class RemoteAnimationTarget implements Parcelable {
pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
pw.print(prefix); pw.print("leash="); pw.println(leash);
pw.print(prefix); pw.print("pictureInPictureParams="); pw.println(pictureInPictureParams);
+ pw.print(prefix); pw.print("windowType="); pw.print(windowType);
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index cc7525b48aa2..56b438350d06 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -163,7 +163,7 @@ public final class RoundedCorner implements Parcelable {
* @hide
*/
public boolean isEmpty() {
- return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
+ return mRadius == 0 || mCenter.x <= 0 || mCenter.y <= 0;
}
private String getPositionString(@Position int position) {
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index 569c287901c7..623d9692ac80 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -181,16 +181,16 @@ public class RoundedCorners implements Parcelable {
boolean hasRoundedCorner;
switch (position) {
case POSITION_TOP_LEFT:
- hasRoundedCorner = radius > insetTop || radius > insetLeft;
+ hasRoundedCorner = radius > insetTop && radius > insetLeft;
break;
case POSITION_TOP_RIGHT:
- hasRoundedCorner = radius > insetTop || radius > insetRight;
+ hasRoundedCorner = radius > insetTop && radius > insetRight;
break;
case POSITION_BOTTOM_RIGHT:
- hasRoundedCorner = radius > insetBottom || radius > insetRight;
+ hasRoundedCorner = radius > insetBottom && radius > insetRight;
break;
case POSITION_BOTTOM_LEFT:
- hasRoundedCorner = radius > insetBottom || radius > insetLeft;
+ hasRoundedCorner = radius > insetBottom && radius > insetLeft;
break;
default:
throw new IllegalArgumentException(
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index 3456e016c42c..a6d786e1db21 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -32,6 +32,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -50,8 +51,9 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
private final Object mLock = new Object();
private final Rect mScrollBounds;
private final Point mPositionInWindow;
- private final CloseGuard mCloseGuard;
private final Executor mUiThread;
+ private final CloseGuard mCloseGuard = new CloseGuard();
+
private ScrollCaptureCallback mLocal;
private IScrollCaptureCallbacks mRemote;
@@ -60,42 +62,38 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
private CancellationSignal mCancellation;
- private volatile boolean mStarted;
- private volatile boolean mConnected;
+ private volatile boolean mActive;
/**
* Constructs a ScrollCaptureConnection.
*
+ * @param uiThread an executor for the UI thread of the containing View
* @param selectedTarget the target the client is controlling
- * @param remote the callbacks to reply to system requests
*
* @hide
*/
public ScrollCaptureConnection(
@NonNull Executor uiThread,
- @NonNull ScrollCaptureTarget selectedTarget,
- @NonNull IScrollCaptureCallbacks remote) {
+ @NonNull ScrollCaptureTarget selectedTarget) {
mUiThread = requireNonNull(uiThread, "<uiThread> must non-null");
requireNonNull(selectedTarget, "<selectedTarget> must non-null");
- mRemote = requireNonNull(remote, "<callbacks> must non-null");
mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()),
"target.getScrollBounds() must be non-null to construct a client");
-
mLocal = selectedTarget.getCallback();
mPositionInWindow = new Point(selectedTarget.getPositionInWindow());
-
- mCloseGuard = new CloseGuard();
- mCloseGuard.open("close");
- mConnected = true;
}
@BinderThread
@Override
- public ICancellationSignal startCapture(Surface surface) throws RemoteException {
- checkConnected();
+ public ICancellationSignal startCapture(@NonNull Surface surface,
+ @NonNull IScrollCaptureCallbacks remote) throws RemoteException {
+
+ mCloseGuard.open("close");
+
if (!surface.isValid()) {
throw new RemoteException(new IllegalArgumentException("surface must be valid"));
}
+ mRemote = requireNonNull(remote, "<callbacks> must non-null");
ICancellationSignal cancellation = CancellationSignal.createTransport();
mCancellation = CancellationSignal.fromTransport(cancellation);
@@ -110,7 +108,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
@UiThread
private void onStartCaptureCompleted() {
- mStarted = true;
+ mActive = true;
try {
mRemote.onCaptureStarted();
} catch (RemoteException e) {
@@ -119,13 +117,11 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
}
}
-
@BinderThread
@Override
public ICancellationSignal requestImage(Rect requestRect) throws RemoteException {
Trace.beginSection("requestImage");
- checkConnected();
- checkStarted();
+ checkActive();
ICancellationSignal cancellation = CancellationSignal.createTransport();
mCancellation = CancellationSignal.fromTransport(cancellation);
@@ -152,8 +148,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
@BinderThread
@Override
public ICancellationSignal endCapture() throws RemoteException {
- checkConnected();
- checkStarted();
+ checkActive();
ICancellationSignal cancellation = CancellationSignal.createTransport();
mCancellation = CancellationSignal.fromTransport(cancellation);
@@ -167,64 +162,48 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
@UiThread
private void onEndCaptureCompleted() {
- synchronized (mLock) {
- mStarted = false;
- try {
+ mActive = false;
+ try {
+ if (mRemote != null) {
mRemote.onCaptureEnded();
- } catch (RemoteException e) {
- Log.w(TAG, "Shutting down due to error: ", e);
- close();
}
+ } catch (RemoteException e) {
+ Log.w(TAG, "Caught exception confirming capture end!", e);
+ } finally {
+ close();
}
}
@BinderThread
@Override
public void close() {
- if (mStarted) {
- Log.w(TAG, "close(): capture is still started?! Ending now.");
-
+ if (mActive) {
+ if (mCancellation != null) {
+ Log.w(TAG, "close(): cancelling pending operation.");
+ mCancellation.cancel();
+ mCancellation = null;
+ }
+ Log.w(TAG, "close(): capture session still active! Ending now.");
// -> UiThread
mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ }));
- mStarted = false;
+ mActive = false;
}
- disconnect();
+ mActive = false;
+ mSession = null;
+ mRemote = null;
+ mLocal = null;
+ mCloseGuard.close();
+ Reference.reachabilityFence(this);
}
- /**
- * Shuts down this client and releases references to dependent objects. No attempt is made
- * to notify the controller, use with caution!
- */
- private void disconnect() {
- synchronized (mLock) {
- mSession = null;
- mConnected = false;
- mStarted = false;
- mRemote = null;
- mLocal = null;
- mCloseGuard.close();
- }
- }
-
- public boolean isConnected() {
- return mConnected;
- }
-
- public boolean isStarted() {
- return mStarted;
- }
-
- private synchronized void checkConnected() throws RemoteException {
- synchronized (mLock) {
- if (!mConnected) {
- throw new RemoteException(new IllegalStateException("Not connected"));
- }
- }
+ @VisibleForTesting
+ public boolean isActive() {
+ return mActive;
}
- private void checkStarted() throws RemoteException {
+ private void checkActive() throws RemoteException {
synchronized (mLock) {
- if (!mStarted) {
+ if (!mActive) {
throw new RemoteException(new IllegalStateException("Not started!"));
}
}
@@ -233,24 +212,16 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
/** @return a string representation of the state of this client */
public String toString() {
return "ScrollCaptureConnection{"
- + "connected=" + mConnected
- + ", started=" + mStarted
+ + "active=" + mActive
+ ", session=" + mSession
+ ", remote=" + mRemote
+ ", local=" + mLocal
+ "}";
}
- @VisibleForTesting
- public CancellationSignal getCancellation() {
- return mCancellation;
- }
-
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
+ mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java
index 564113edb3c7..8808827b248a 100644
--- a/core/java/android/view/ScrollCaptureResponse.java
+++ b/core/java/android/view/ScrollCaptureResponse.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcelable;
+import android.os.RemoteException;
import com.android.internal.util.DataClass;
@@ -57,11 +58,22 @@ public class ScrollCaptureResponse implements Parcelable {
@DataClass.PluralOf("message")
private ArrayList<String> mMessages = new ArrayList<>();
- /** Whether a connection has been returned. */
+ /** Whether an active connection is present. */
public boolean isConnected() {
- return mConnection != null;
+ return mConnection != null && mConnection.asBinder().isBinderAlive();
}
+ /** Closes a connection returned with this response. */
+ public void close() {
+ if (mConnection != null) {
+ try {
+ mConnection.close();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ mConnection = null;
+ }
+ }
@@ -367,10 +379,10 @@ public class ScrollCaptureResponse implements Parcelable {
}
@DataClass.Generated(
- time = 1612282689462L,
+ time = 1614833185795L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 03dd10050724..0167147a1067 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -200,7 +200,8 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSyncInputWindows(long transactionObj);
private static native boolean nativeGetDisplayBrightnessSupport(IBinder displayToken);
private static native boolean nativeSetDisplayBrightness(IBinder displayToken,
- float brightness);
+ float sdrBrightness, float sdrBrightnessNits, float displayBrightness,
+ float displayBrightnessNits);
private static native long nativeReadTransactionFromParcel(Parcel in);
private static native void nativeWriteTransactionToParcel(long nativeObject, Parcel out);
private static native void nativeSetShadowRadius(long transactionObj, long nativeObject,
@@ -2405,13 +2406,50 @@ public final class SurfaceControl implements Parcelable {
* @hide
*/
public static boolean setDisplayBrightness(IBinder displayToken, float brightness) {
+ return setDisplayBrightness(displayToken, brightness, -1, brightness, -1);
+ }
+
+ /**
+ * Sets the brightness of a display.
+ *
+ * @param displayToken
+ * The token for the display whose brightness is set.
+ * @param sdrBrightness
+ * A number between 0.0f (minimum brightness) and 1.0f (maximum brightness), or -1.0f to
+ * turn the backlight off. Specifies the desired brightness of SDR content.
+ * @param sdrBrightnessNits
+ * The value of sdrBrightness converted to calibrated nits. -1 if this isn't available.
+ * @param displayBrightness
+ * A number between 0.0f (minimum brightness) and 1.0f (maximum brightness), or
+ * -1.0f to turn the backlight off. Specifies the desired brightness of the display itself,
+ * used directly for HDR content.
+ * @param displayBrightnessNits
+ * The value of displayBrightness converted to calibrated nits. -1 if this isn't
+ * available.
+ *
+ * @return Whether the method succeeded or not.
+ *
+ * @throws IllegalArgumentException if:
+ * - displayToken is null;
+ * - brightness is NaN or greater than 1.0f.
+ *
+ * @hide
+ */
+ public static boolean setDisplayBrightness(IBinder displayToken, float sdrBrightness,
+ float sdrBrightnessNits, float displayBrightness, float displayBrightnessNits) {
Objects.requireNonNull(displayToken);
- if (Float.isNaN(brightness) || brightness > 1.0f
- || (brightness < 0.0f && brightness != -1.0f)) {
- throw new IllegalArgumentException("brightness must be a number between 0.0f and 1.0f,"
- + " or -1 to turn the backlight off: " + brightness);
- }
- return nativeSetDisplayBrightness(displayToken, brightness);
+ if (Float.isNaN(displayBrightness) || displayBrightness > 1.0f
+ || (displayBrightness < 0.0f && displayBrightness != -1.0f)) {
+ throw new IllegalArgumentException("displayBrightness must be a number between 0.0f "
+ + " and 1.0f, or -1 to turn the backlight off: " + displayBrightness);
+ }
+ if (Float.isNaN(sdrBrightness) || sdrBrightness > 1.0f
+ || (sdrBrightness < 0.0f && sdrBrightness != -1.0f)) {
+ throw new IllegalArgumentException("sdrBrightness must be a number between 0.0f "
+ + "and 1.0f, or -1 to turn the backlight off: " + displayBrightness);
+ }
+ return nativeSetDisplayBrightness(displayToken, sdrBrightness, sdrBrightnessNits,
+ displayBrightness, displayBrightnessNits);
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9688c677b900..3ffe0c660f9a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1390,14 +1390,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
viewRoot.mergeWithNextTransaction(mRtTransaction, frameNumber);
return;
}
-
- // Otherwise if the if the ViewRoot is not null, use deferred transaction instead.
- if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()
- && mSurfaceControl != null) {
- mRtTransaction.deferTransactionUntil(mSurfaceControl,
- viewRoot.getSurfaceControl(), frameNumber);
- }
- mRtTransaction.apply();
}
private Rect mRTLastReportedPosition = new Rect();
@@ -1470,12 +1462,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
*/
synchronized (mSurfaceControlLock) {
final ViewRootImpl viewRoot = getViewRootImpl();
- boolean deferTransaction = frameNumber > 0 && viewRoot != null
- && viewRoot.mSurface.isValid() && !useBLASTSync(viewRoot);
- if (deferTransaction) {
- mRtTransaction.deferTransactionUntil(mSurfaceControl,
- viewRoot.getSurfaceControl(), frameNumber);
- }
mRtTransaction.hide(mSurfaceControl);
if (mRtReleaseSurfaces) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9fc415d6401f..d462f5844a70 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5356,7 +5356,7 @@ public final class ViewRootImpl implements ViewParent,
updateLocationInParentDisplay(msg.arg1, msg.arg2);
} break;
case MSG_REQUEST_SCROLL_CAPTURE:
- handleScrollCaptureRequest((IScrollCaptureCallbacks) msg.obj);
+ handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
}
}
@@ -8572,6 +8572,17 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
+ public void onDragEvent(boolean isExiting, float x, float y) {
+ // force DRAG_EXITED_EVENT if appropriate
+ DragEvent event = DragEvent.obtain(
+ isExiting ? DragEvent.ACTION_DRAG_EXITED : DragEvent.ACTION_DRAG_LOCATION,
+ x, y, 0 /* offsetX */, 0 /* offsetY */, null/* localState */,
+ null/* description */, null /* data */, null /* dragSurface */,
+ null /* dragAndDropPermissions */, false /* result */);
+ dispatchDragEvent(event);
+ }
+
+ @Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
@@ -9256,10 +9267,10 @@ public final class ViewRootImpl implements ViewParent,
/**
* Dispatches a scroll capture request to the view hierarchy on the ui thread.
*
- * @param callbacks for replies
+ * @param listener for the response
*/
- public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
- mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, callbacks).sendToTarget();
+ public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) {
+ mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget();
}
/**
@@ -9306,10 +9317,10 @@ public final class ViewRootImpl implements ViewParent,
* A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)}
* will follow.
*
- * @param callbacks to receive responses
+ * @param listener to receive responses
* @see ScrollCaptureTargetSelector
*/
- public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
+ public void handleScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) {
ScrollCaptureSearchResults results =
new ScrollCaptureSearchResults(mContext.getMainExecutor());
@@ -9324,7 +9335,7 @@ public final class ViewRootImpl implements ViewParent,
getChildVisibleRect(rootView, rect, point);
rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget);
}
- Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results);
+ Runnable onComplete = () -> dispatchScrollCaptureSearchResponse(listener, results);
results.setOnCompleteListener(onComplete);
if (!results.isComplete()) {
mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout());
@@ -9332,8 +9343,8 @@ public final class ViewRootImpl implements ViewParent,
}
/** Called by {@link #handleScrollCaptureRequest} when a result is returned */
- private void dispatchScrollCaptureSearchResult(
- @NonNull IScrollCaptureCallbacks callbacks,
+ private void dispatchScrollCaptureSearchResponse(
+ @NonNull IScrollCaptureResponseListener listener,
@NonNull ScrollCaptureSearchResults results) {
ScrollCaptureTarget selectedTarget = results.getTopResult();
@@ -9350,7 +9361,7 @@ public final class ViewRootImpl implements ViewParent,
if (selectedTarget == null) {
response.setDescription("No scrollable targets found in window");
try {
- callbacks.onScrollCaptureResponse(response.build());
+ listener.onScrollCaptureResponse(response.build());
} catch (RemoteException e) {
Log.e(TAG, "Failed to send scroll capture search result", e);
}
@@ -9376,11 +9387,11 @@ public final class ViewRootImpl implements ViewParent,
// Create a connection and return it to the caller
ScrollCaptureConnection connection = new ScrollCaptureConnection(
- mView.getContext().getMainExecutor(), selectedTarget, callbacks);
+ mView.getContext().getMainExecutor(), selectedTarget);
response.setConnection(connection);
try {
- callbacks.onScrollCaptureResponse(response.build());
+ listener.onScrollCaptureResponse(response.build());
} catch (RemoteException e) {
if (DEBUG_SCROLL_CAPTURE) {
Log.w(TAG, "Failed to send scroll capture search response.", e);
@@ -9680,10 +9691,10 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
- viewAncestor.dispatchScrollCaptureRequest(callbacks);
+ viewAncestor.dispatchScrollCaptureRequest(listener);
}
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index cf5ec8de0362..c814e5add1b7 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2619,10 +2619,10 @@ public abstract class Window {
/**
* System request to begin scroll capture.
*
- * @param callbacks to receive responses
+ * @param listener to receive the response
* @hide
*/
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
}
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7338c7d9a581..04512c9abc0a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -81,6 +81,7 @@ import static android.view.WindowLayoutParamsProto.X;
import static android.view.WindowLayoutParamsProto.Y;
import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -121,6 +122,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -864,6 +866,33 @@ public interface WindowManager extends ViewManager {
}
/**
+ * Adds a listener, which will be called when cross-window blurs are enabled/disabled at
+ * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius})
+ * and window background blur (see {@link Window#setBackgroundBlurRadius}).
+ *
+ * Cross-window blur might not be supported by some devices due to GPU limitations. It can also
+ * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+ * when minimal post processing is requested. In such situations, no blur will be computed or
+ * drawn, so the blur target area will not be blurred. To handle this, the app might want to
+ * change its theme to one that does not use blurs.
+ *
+ * If the listener is added successfully, it will be called immediately with the current
+ * cross-window blur enabled state.
+ *
+ * @param executor {@link Executor} to handle the listener callback
+ * @param listener the listener to be added. It will be called back with a boolean parameter,
+ * which is true if cross-window blur is enabled and false if it is disabled
+ *
+ * @see #removeCrossWindowBlurEnabledListener
+ * @see #isCrossWindowBlurEnabled
+ * @see LayoutParams#setBlurBehindRadius
+ * @see Window#setBackgroundBlurRadius
+ */
+ default void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ }
+
+ /**
* Removes a listener, previously added with {@link #addCrossWindowBlurEnabledListener}
*
* @param listener the listener to be removed
@@ -873,6 +902,20 @@ public interface WindowManager extends ViewManager {
default void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
}
+ /**
+ * Disables cross-window blurs device-wide. This includes window blur behind
+ * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur
+ * (see {@link Window#setBackgroundBlurRadius}).
+ *
+ * @param disable specifies whether to disable the blur. Note that calling this
+ * with 'disable=false' will not enable blurs if there is something
+ * else disabling blurs.
+ * @hide
+ */
+ @TestApi
+ default void setForceCrossWindowBlurDisabled(boolean disable) {
+ }
+
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index b39870738d68..8dce852a2d62 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
@@ -40,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -310,11 +312,26 @@ public final class WindowManagerImpl implements WindowManager {
@Override
public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
- CrossWindowBlurListeners.getInstance().addListener(listener);
+ addCrossWindowBlurEnabledListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ CrossWindowBlurListeners.getInstance().addListener(executor, listener);
}
@Override
public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
CrossWindowBlurListeners.getInstance().removeListener(listener);
}
+
+ @Override
+ public void setForceCrossWindowBlurDisabled(boolean disable) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .setForceCrossWindowBlurDisabled(disable);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9523bcdb8e39..ed840ce3061b 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -70,7 +70,7 @@ import java.util.function.Consumer;
* <p>Content capture provides real-time, continuous capture of application activity, display and
* events to an intelligence service that is provided by the Android system. The intelligence
* service then uses that info to mediate and speed user journey through different apps. For
- * example, when the user receives a restaurant address in a chat app and switchs to a map app
+ * example, when the user receives a restaurant address in a chat app and switches to a map app
* to search for that restaurant, the intelligence service could offer an autofill dialog to
* let the user automatically select its address.
*
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index b8893cee834d..27c637bb59dd 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.util.Size;
import android.util.Slog;
import android.view.SurfaceControlViewHost;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.inline.InlineContentView;
@@ -38,6 +39,7 @@ import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.internal.view.inline.InlineTooltipUi;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
@@ -75,6 +77,15 @@ public final class InlineSuggestion implements Parcelable {
private InlineContentCallbackImpl mInlineContentCallback;
/**
+ * Used to show up the inline suggestion tooltip.
+ *
+ * @hide
+ */
+ @Nullable
+ @DataClass.ParcelWith(InlineTooltipUiParceling.class)
+ private InlineTooltipUi mInlineTooltipUi;
+
+ /**
* Creates a new {@link InlineSuggestion}, for testing purpose.
*
* @hide
@@ -82,7 +93,8 @@ public final class InlineSuggestion implements Parcelable {
@TestApi
@NonNull
public static InlineSuggestion newInlineSuggestion(@NonNull InlineSuggestionInfo info) {
- return new InlineSuggestion(info, null, /* inlineContentCallback */ null);
+ return new InlineSuggestion(info, null, /* inlineContentCallback */ null,
+ /* inlineTooltipUi */ null);
}
/**
@@ -92,7 +104,7 @@ public final class InlineSuggestion implements Parcelable {
*/
public InlineSuggestion(@NonNull InlineSuggestionInfo info,
@Nullable IInlineContentProvider contentProvider) {
- this(info, contentProvider, /* inlineContentCallback */ null);
+ this(info, contentProvider, /* inlineContentCallback */ null, /* inlineTooltipUi */ null);
}
/**
@@ -136,9 +148,21 @@ public final class InlineSuggestion implements Parcelable {
"size is neither between min:" + minSize + " and max:" + maxSize
+ ", nor wrap_content");
}
- mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback);
+
+ InlineSuggestion toolTip = mInfo.getTooltip();
+ if (toolTip != null) {
+ if (mInlineTooltipUi == null) {
+ mInlineTooltipUi = new InlineTooltipUi(context);
+ }
+ } else {
+ mInlineTooltipUi = null;
+ }
+
+ mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback,
+ mInlineTooltipUi);
if (mContentProvider == null) {
callbackExecutor.execute(() -> callback.accept(/* view */ null));
+ mInlineTooltipUi = null;
return;
}
try {
@@ -148,6 +172,13 @@ public final class InlineSuggestion implements Parcelable {
Slog.w(TAG, "Error creating suggestion content surface: " + e);
callbackExecutor.execute(() -> callback.accept(/* view */ null));
}
+ if (toolTip == null) return;
+
+ final Size tooltipSize = new Size(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ mInfo.getTooltip().inflate(context, tooltipSize, callbackExecutor, view -> {
+ Handler.getMain().post(() -> mInlineTooltipUi.setTooltipView(view));
+ });
}
/**
@@ -162,12 +193,13 @@ public final class InlineSuggestion implements Parcelable {
}
private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context,
- Executor callbackExecutor, Consumer<InlineContentView> callback) {
+ Executor callbackExecutor, Consumer<InlineContentView> callback,
+ InlineTooltipUi inlineTooltipUi) {
if (mInlineContentCallback != null) {
throw new IllegalStateException("Already called #inflate()");
}
return new InlineContentCallbackImpl(context, mContentProvider, callbackExecutor,
- callback);
+ callback, inlineTooltipUi);
}
/**
@@ -267,14 +299,19 @@ public final class InlineSuggestion implements Parcelable {
@Nullable
private Consumer<SurfaceControlViewHost.SurfacePackage> mSurfacePackageConsumer;
+ @Nullable
+ private InlineTooltipUi mInlineTooltipUi;
+
InlineContentCallbackImpl(@NonNull Context context,
@Nullable IInlineContentProvider inlineContentProvider,
@NonNull @CallbackExecutor Executor callbackExecutor,
- @NonNull Consumer<InlineContentView> callback) {
+ @NonNull Consumer<InlineContentView> callback,
+ @Nullable InlineTooltipUi inlineTooltipUi) {
mContext = context;
mInlineContentProvider = inlineContentProvider;
mCallbackExecutor = callbackExecutor;
mCallback = callback;
+ mInlineTooltipUi = inlineTooltipUi;
}
@BinderThread
@@ -305,6 +342,17 @@ public final class InlineSuggestion implements Parcelable {
mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
} else {
mView = new InlineContentView(mContext);
+ if (mInlineTooltipUi != null) {
+ mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mInlineTooltipUi != null) {
+ mInlineTooltipUi.update(mView);
+ }
+ }
+ });
+ }
mView.setLayoutParams(new ViewGroup.LayoutParams(width, height));
mView.setChildSurfacePackageUpdater(getSurfacePackageUpdater());
mCallbackExecutor.execute(() -> mCallback.accept(mView));
@@ -425,10 +473,25 @@ public final class InlineSuggestion implements Parcelable {
}
}
+ /**
+ * This class used to provide parcelling logic for InlineContentCallbackImpl. It's intended to
+ * make this parcelling a no-op, since it can't be parceled and we don't need to parcel it.
+ */
+ private static class InlineTooltipUiParceling implements
+ Parcelling<InlineTooltipUi> {
+ @Override
+ public void parcel(InlineTooltipUi item, Parcel dest, int parcelFlags) {
+ }
+
+ @Override
+ public InlineTooltipUi unparcel(Parcel source) {
+ return null;
+ }
+ }
- // Code below generated by codegen v1.0.15.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -446,18 +509,22 @@ public final class InlineSuggestion implements Parcelable {
*
* @param inlineContentCallback
* Used to keep a strong reference to the callback so it doesn't get garbage collected.
+ * @param inlineTooltipUi
+ * Used to show up the inline suggestion tooltip.
* @hide
*/
@DataClass.Generated.Member
public InlineSuggestion(
@NonNull InlineSuggestionInfo info,
@Nullable IInlineContentProvider contentProvider,
- @Nullable InlineContentCallbackImpl inlineContentCallback) {
+ @Nullable InlineContentCallbackImpl inlineContentCallback,
+ @Nullable InlineTooltipUi inlineTooltipUi) {
this.mInfo = info;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInfo);
this.mContentProvider = contentProvider;
this.mInlineContentCallback = inlineContentCallback;
+ this.mInlineTooltipUi = inlineTooltipUi;
// onConstructed(); // You can define this method to get a callback
}
@@ -485,6 +552,16 @@ public final class InlineSuggestion implements Parcelable {
return mInlineContentCallback;
}
+ /**
+ * Used to show up the inline suggestion tooltip.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable InlineTooltipUi getInlineTooltipUi() {
+ return mInlineTooltipUi;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -494,7 +571,8 @@ public final class InlineSuggestion implements Parcelable {
return "InlineSuggestion { " +
"info = " + mInfo + ", " +
"contentProvider = " + mContentProvider + ", " +
- "inlineContentCallback = " + mInlineContentCallback +
+ "inlineContentCallback = " + mInlineContentCallback + ", " +
+ "inlineTooltipUi = " + mInlineTooltipUi +
" }";
}
@@ -513,7 +591,8 @@ public final class InlineSuggestion implements Parcelable {
return true
&& java.util.Objects.equals(mInfo, that.mInfo)
&& java.util.Objects.equals(mContentProvider, that.mContentProvider)
- && java.util.Objects.equals(mInlineContentCallback, that.mInlineContentCallback);
+ && java.util.Objects.equals(mInlineContentCallback, that.mInlineContentCallback)
+ && java.util.Objects.equals(mInlineTooltipUi, that.mInlineTooltipUi);
}
@Override
@@ -526,6 +605,7 @@ public final class InlineSuggestion implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mInfo);
_hash = 31 * _hash + java.util.Objects.hashCode(mContentProvider);
_hash = 31 * _hash + java.util.Objects.hashCode(mInlineContentCallback);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipUi);
return _hash;
}
@@ -540,6 +620,17 @@ public final class InlineSuggestion implements Parcelable {
}
}
+ @DataClass.Generated.Member
+ static Parcelling<InlineTooltipUi> sParcellingForInlineTooltipUi =
+ Parcelling.Cache.get(
+ InlineTooltipUiParceling.class);
+ static {
+ if (sParcellingForInlineTooltipUi == null) {
+ sParcellingForInlineTooltipUi = Parcelling.Cache.put(
+ new InlineTooltipUiParceling());
+ }
+ }
+
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -549,10 +640,12 @@ public final class InlineSuggestion implements Parcelable {
byte flg = 0;
if (mContentProvider != null) flg |= 0x2;
if (mInlineContentCallback != null) flg |= 0x4;
+ if (mInlineTooltipUi != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeTypedObject(mInfo, flags);
if (mContentProvider != null) dest.writeStrongInterface(mContentProvider);
sParcellingForInlineContentCallback.parcel(mInlineContentCallback, dest, flags);
+ sParcellingForInlineTooltipUi.parcel(mInlineTooltipUi, dest, flags);
}
@Override
@@ -570,12 +663,14 @@ public final class InlineSuggestion implements Parcelable {
InlineSuggestionInfo info = (InlineSuggestionInfo) in.readTypedObject(InlineSuggestionInfo.CREATOR);
IInlineContentProvider contentProvider = (flg & 0x2) == 0 ? null : IInlineContentProvider.Stub.asInterface(in.readStrongBinder());
InlineContentCallbackImpl inlineContentCallback = sParcellingForInlineContentCallback.unparcel(in);
+ InlineTooltipUi inlineTooltipUi = sParcellingForInlineTooltipUi.unparcel(in);
this.mInfo = info;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInfo);
this.mContentProvider = contentProvider;
this.mInlineContentCallback = inlineContentCallback;
+ this.mInlineTooltipUi = inlineTooltipUi;
// onConstructed(); // You can define this method to get a callback
}
@@ -595,10 +690,10 @@ public final class InlineSuggestion implements Parcelable {
};
@DataClass.Generated(
- time = 1589396017700L,
- codegenVersion = "1.0.15",
+ time = 1615562097666L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineTooltipUiParceling.class) com.android.internal.view.inline.InlineTooltipUi mInlineTooltipUi\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>,com.android.internal.view.inline.InlineTooltipUi)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 10fd0e036814..5798614b8c9f 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -73,6 +73,11 @@ public final class InlineSuggestionInfo implements Parcelable {
private final boolean mPinned;
/**
+ * @hide
+ */
+ private final @Nullable InlineSuggestion mTooltip;
+
+ /**
* Creates a new {@link InlineSuggestionInfo}, for testing purpose.
*
* @hide
@@ -84,12 +89,30 @@ public final class InlineSuggestionInfo implements Parcelable {
@NonNull @Source String source,
@SuppressLint("NullableCollection")
@Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
- return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
+ return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned,
+ null);
+ }
+
+ /**
+ * Creates a new {@link InlineSuggestionInfo}, for testing purpose.
+ *
+ * @hide
+ */
+ @NonNull
+ public static InlineSuggestionInfo newInlineSuggestionInfo(
+ @NonNull InlinePresentationSpec presentationSpec,
+ @NonNull @Source String source,
+ @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned,
+ @Nullable InlineSuggestion tooltip) {
+ return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned,
+ tooltip);
}
- // Code below generated by codegen v1.0.20.
+
+
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -141,7 +164,8 @@ public final class InlineSuggestionInfo implements Parcelable {
@NonNull @Source String source,
@Nullable String[] autofillHints,
@NonNull @Type String type,
- boolean pinned) {
+ boolean pinned,
+ @Nullable InlineSuggestion tooltip) {
this.mInlinePresentationSpec = inlinePresentationSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInlinePresentationSpec);
@@ -171,6 +195,7 @@ public final class InlineSuggestionInfo implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mType);
this.mPinned = pinned;
+ this.mTooltip = tooltip;
// onConstructed(); // You can define this method to get a callback
}
@@ -215,6 +240,14 @@ public final class InlineSuggestionInfo implements Parcelable {
return mPinned;
}
+ /**
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable InlineSuggestion getTooltip() {
+ return mTooltip;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -226,7 +259,8 @@ public final class InlineSuggestionInfo implements Parcelable {
"source = " + mSource + ", " +
"autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " +
"type = " + mType + ", " +
- "pinned = " + mPinned +
+ "pinned = " + mPinned + ", " +
+ "tooltip = " + mTooltip +
" }";
}
@@ -247,7 +281,8 @@ public final class InlineSuggestionInfo implements Parcelable {
&& java.util.Objects.equals(mSource, that.mSource)
&& java.util.Arrays.equals(mAutofillHints, that.mAutofillHints)
&& java.util.Objects.equals(mType, that.mType)
- && mPinned == that.mPinned;
+ && mPinned == that.mPinned
+ && java.util.Objects.equals(mTooltip, that.mTooltip);
}
@Override
@@ -262,6 +297,7 @@ public final class InlineSuggestionInfo implements Parcelable {
_hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints);
_hash = 31 * _hash + java.util.Objects.hashCode(mType);
_hash = 31 * _hash + Boolean.hashCode(mPinned);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mTooltip);
return _hash;
}
@@ -274,11 +310,13 @@ public final class InlineSuggestionInfo implements Parcelable {
byte flg = 0;
if (mPinned) flg |= 0x10;
if (mAutofillHints != null) flg |= 0x4;
+ if (mTooltip != null) flg |= 0x20;
dest.writeByte(flg);
dest.writeTypedObject(mInlinePresentationSpec, flags);
dest.writeString(mSource);
if (mAutofillHints != null) dest.writeStringArray(mAutofillHints);
dest.writeString(mType);
+ if (mTooltip != null) dest.writeTypedObject(mTooltip, flags);
}
@Override
@@ -298,6 +336,7 @@ public final class InlineSuggestionInfo implements Parcelable {
String source = in.readString();
String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray();
String type = in.readString();
+ InlineSuggestion tooltip = (flg & 0x20) == 0 ? null : (InlineSuggestion) in.readTypedObject(InlineSuggestion.CREATOR);
this.mInlinePresentationSpec = inlinePresentationSpec;
com.android.internal.util.AnnotationValidations.validate(
@@ -328,6 +367,7 @@ public final class InlineSuggestionInfo implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mType);
this.mPinned = pinned;
+ this.mTooltip = tooltip;
// onConstructed(); // You can define this method to get a callback
}
@@ -347,10 +387,10 @@ public final class InlineSuggestionInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1604456249219L,
- codegenVersion = "1.0.20",
+ time = 1614287616672L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestion mTooltip\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic static @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean,android.view.inputmethod.InlineSuggestion)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 0ab4e05227ba..e1e175512edc 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -106,6 +106,27 @@ public final class InlineSuggestionsRequest implements Parcelable {
private int mHostDisplayId;
/**
+ * Specifies the UI specification for the inline suggestion tooltip in the response.
+ */
+ private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+
+ /**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mServiceSupported;
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mClientSupported;
+
+ /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -151,6 +172,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
for (int i = 0; i < mInlinePresentationSpecs.size(); i++) {
mInlinePresentationSpecs.get(i).filterContentTypes();
}
+
+ if (mInlineTooltipPresentationSpec != null) {
+ mInlineTooltipPresentationSpec.filterContentTypes();
+ }
}
private static int defaultMaxSuggestionCount() {
@@ -161,6 +186,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
return ActivityThread.currentPackageName();
}
+ private static InlinePresentationSpec defaultInlineTooltipPresentationSpec() {
+ return null;
+ }
+
/**
* The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when
* it's not set, instead of the default system locale.
@@ -191,6 +220,14 @@ public final class InlineSuggestionsRequest implements Parcelable {
return Bundle.EMPTY;
}
+ private static boolean defaultServiceSupported() {
+ return true;
+ }
+
+ private static boolean defaultClientSupported() {
+ return true;
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setInlinePresentationSpecs(
@@ -203,6 +240,16 @@ public final class InlineSuggestionsRequest implements Parcelable {
abstract Builder setHostDisplayId(int value);
}
+ /** @hide */
+ public boolean isServiceSupported() {
+ return mServiceSupported;
+ }
+
+ /** @hide */
+ public boolean isClientSupported() {
+ return mClientSupported;
+ }
+
// Code below generated by codegen v1.0.22.
@@ -226,7 +273,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
@NonNull LocaleList supportedLocales,
@NonNull Bundle extras,
@Nullable IBinder hostInputToken,
- int hostDisplayId) {
+ int hostDisplayId,
+ @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
+ boolean serviceSupported,
+ boolean clientSupported) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -242,6 +292,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
NonNull.class, null, mExtras);
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
+ this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -324,6 +377,16 @@ public final class InlineSuggestionsRequest implements Parcelable {
return mHostDisplayId;
}
+ /**
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
+ */
+ @DataClass.Generated.Member
+ public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
+ return mInlineTooltipPresentationSpec;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -337,7 +400,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
"supportedLocales = " + mSupportedLocales + ", " +
"extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
- "hostDisplayId = " + mHostDisplayId +
+ "hostDisplayId = " + mHostDisplayId + ", " +
+ "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
+ "serviceSupported = " + mServiceSupported + ", " +
+ "clientSupported = " + mClientSupported +
" }";
}
@@ -360,7 +426,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
&& java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales)
&& extrasEquals(that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
- && mHostDisplayId == that.mHostDisplayId;
+ && mHostDisplayId == that.mHostDisplayId
+ && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
+ && mServiceSupported == that.mServiceSupported
+ && mClientSupported == that.mClientSupported;
}
@Override
@@ -377,6 +446,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
_hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + mHostDisplayId;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
+ _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
+ _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
return _hash;
}
@@ -386,9 +458,12 @@ public final class InlineSuggestionsRequest implements Parcelable {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- byte flg = 0;
+ int flg = 0;
+ if (mServiceSupported) flg |= 0x100;
+ if (mClientSupported) flg |= 0x200;
if (mHostInputToken != null) flg |= 0x20;
- dest.writeByte(flg);
+ if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
+ dest.writeInt(flg);
dest.writeInt(mMaxSuggestionCount);
dest.writeParcelableList(mInlinePresentationSpecs, flags);
dest.writeString(mHostPackageName);
@@ -396,6 +471,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
dest.writeBundle(mExtras);
parcelHostInputToken(dest, flags);
dest.writeInt(mHostDisplayId);
+ if (mInlineTooltipPresentationSpec != null) dest.writeTypedObject(mInlineTooltipPresentationSpec, flags);
}
@Override
@@ -409,7 +485,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- byte flg = in.readByte();
+ int flg = in.readInt();
+ boolean serviceSupported = (flg & 0x100) != 0;
+ boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
@@ -418,6 +496,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
Bundle extras = in.readBundle();
IBinder hostInputToken = unparcelHostInputToken(in);
int hostDisplayId = in.readInt();
+ InlinePresentationSpec inlineTooltipPresentationSpec = (flg & 0x80) == 0 ? null : (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
@@ -434,6 +513,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
NonNull.class, null, mExtras);
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
+ this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -466,6 +548,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @NonNull Bundle mExtras;
private @Nullable IBinder mHostInputToken;
private int mHostDisplayId;
+ private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+ private boolean mServiceSupported;
+ private boolean mClientSupported;
private long mBuilderFieldsSet = 0L;
@@ -597,10 +682,51 @@ public final class InlineSuggestionsRequest implements Parcelable {
return this;
}
+ /**
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mInlineTooltipPresentationSpec = value;
+ return this;
+ }
+
+ /**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setServiceSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mServiceSupported = value;
+ return this;
+ }
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setClientSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mClientSupported = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x400; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -620,6 +746,15 @@ public final class InlineSuggestionsRequest implements Parcelable {
if ((mBuilderFieldsSet & 0x40) == 0) {
mHostDisplayId = defaultHostDisplayId();
}
+ if ((mBuilderFieldsSet & 0x80) == 0) {
+ mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
+ }
+ if ((mBuilderFieldsSet & 0x100) == 0) {
+ mServiceSupported = defaultServiceSupported();
+ }
+ if ((mBuilderFieldsSet & 0x200) == 0) {
+ mClientSupported = defaultClientSupported();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mInlinePresentationSpecs,
@@ -627,12 +762,15 @@ public final class InlineSuggestionsRequest implements Parcelable {
mSupportedLocales,
mExtras,
mHostInputToken,
- mHostDisplayId);
+ mHostDisplayId,
+ mInlineTooltipPresentationSpec,
+ mServiceSupported,
+ mClientSupported);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x400) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -640,10 +778,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1612206506050L,
+ time = 1615798784918L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 5d876a6f62d3..cc533eb3b5d7 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -60,6 +60,7 @@ import java.util.List;
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
+ * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
@@ -118,6 +119,11 @@ public final class InputMethodInfo implements Parcelable {
private final boolean mInlineSuggestionsEnabled;
/**
+ * The flag whether this IME suppresses spell checker.
+ */
+ private final boolean mSuppressesSpellChecker;
+
+ /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -160,6 +166,7 @@ public final class InputMethodInfo implements Parcelable {
boolean isAuxIme = true;
boolean supportsSwitchingToNextInputMethod = false; // false as default
boolean inlineSuggestionsEnabled = false; // false as default
+ boolean suppressesSpellChecker = false; // false as default
mForceDefault = false;
PackageManager pm = context.getPackageManager();
@@ -203,6 +210,8 @@ public final class InputMethodInfo implements Parcelable {
false);
inlineSuggestionsEnabled = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
+ suppressesSpellChecker = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -274,6 +283,7 @@ public final class InputMethodInfo implements Parcelable {
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+ mSuppressesSpellChecker = suppressesSpellChecker;
mIsVrOnly = isVrOnly;
}
@@ -284,6 +294,7 @@ public final class InputMethodInfo implements Parcelable {
mIsAuxIme = source.readInt() == 1;
mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
mInlineSuggestionsEnabled = source.readInt() == 1;
+ mSuppressesSpellChecker = source.readBoolean();
mIsVrOnly = source.readBoolean();
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
@@ -342,6 +353,7 @@ public final class InputMethodInfo implements Parcelable {
mForceDefault = forceDefault;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+ mSuppressesSpellChecker = false;
mIsVrOnly = isVrOnly;
}
@@ -494,7 +506,8 @@ public final class InputMethodInfo implements Parcelable {
+ " mSettingsActivityName=" + mSettingsActivityName
+ " mIsVrOnly=" + mIsVrOnly
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
- + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled);
+ + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ + " mSuppressesSpellChecker=" + mSuppressesSpellChecker);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -563,6 +576,13 @@ public final class InputMethodInfo implements Parcelable {
}
/**
+ * Return {@code true} if this input method suppresses spell checker.
+ */
+ public boolean suppressesSpellChecker() {
+ return mSuppressesSpellChecker;
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -576,6 +596,7 @@ public final class InputMethodInfo implements Parcelable {
dest.writeInt(mIsAuxIme ? 1 : 0);
dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0);
+ dest.writeBoolean(mSuppressesSpellChecker);
dest.writeBoolean(mIsVrOnly);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a8fff8bb6a43..ff4d671bf902 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -106,7 +106,6 @@ import com.android.internal.view.InputBindResult;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
@@ -413,7 +412,7 @@ public final class InputMethodManager {
* The InputConnection that was last retrieved from the served view.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- ControlledInputConnectionWrapper mServedInputConnectionWrapper;
+ IInputConnectionWrapper mServedInputConnectionWrapper;
/**
* The completions that were last provided by the served view.
*/
@@ -445,6 +444,13 @@ public final class InputMethodManager {
*/
private Matrix mActivityViewToScreenMatrix = null;
+ /**
+ * As reported by {@link InputBindResult}. This value is determined by
+ * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecking}.
+ */
+ @GuardedBy("mH")
+ private boolean mIsInputMethodSuppressingSpellChecker = false;
+
// -----------------------------------------------------------
/**
@@ -740,8 +746,7 @@ public final class InputMethodManager {
/**
* Checks whether the active input connection (if any) is for the given view.
*
- * TODO(b/160968797): Remove this method and move mServedInputConnectionWrapper to
- * ImeFocusController.
+ * TODO(b/182259171): Clean-up hasActiveConnection to simplify the logic.
*
* Note that this method is only intended for restarting input after focus gain
* (e.g. b/160391516), DO NOT leverage this method to do another check.
@@ -755,7 +760,7 @@ public final class InputMethodManager {
return mServedInputConnectionWrapper != null
&& mServedInputConnectionWrapper.isActive()
- && mServedInputConnectionWrapper.mServedView.get() == view;
+ && mServedInputConnectionWrapper.getServedView() == view;
}
}
}
@@ -860,6 +865,8 @@ public final class InputMethodManager {
mCurId = res.id;
mBindSequence = res.sequence;
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+ mIsInputMethodSuppressingSpellChecker =
+ res.isInputMethodSuppressingSpellChecker;
}
startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
@@ -1022,77 +1029,6 @@ public final class InputMethodManager {
}
}
- private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
- private final InputMethodManager mParentInputMethodManager;
- private final WeakReference<View> mServedView;
-
- ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn,
- InputMethodManager inputMethodManager, View servedView) {
- super(icLooper, conn);
- mParentInputMethodManager = inputMethodManager;
- mServedView = new WeakReference<>(servedView);
- }
-
- @Override
- public boolean isActive() {
- return mParentInputMethodManager.mActive && !isFinished();
- }
-
- @Override
- public InputMethodManager getIMM() {
- return mParentInputMethodManager;
- }
-
- void deactivate() {
- if (isFinished()) {
- // This is a small performance optimization. Still only the 1st call of
- // reportFinish() will take effect.
- return;
- }
- closeConnection();
-
- // Notify the app that the InputConnection was closed.
- final View servedView = mServedView.get();
- if (servedView != null) {
- final Handler handler = servedView.getHandler();
- // The handler is null if the view is already detached. When that's the case, for
- // now, we simply don't dispatch this callback.
- if (handler != null) {
- if (DEBUG) {
- Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
- }
- if (handler.getLooper().isCurrentThread()) {
- servedView.onInputConnectionClosedInternal();
- } else {
- handler.post(servedView::onInputConnectionClosedInternal);
- }
- }
- }
- }
-
- @Override
- public String toString() {
- return "ControlledInputConnectionWrapper{"
- + "connection=" + getInputConnection()
- + " finished=" + isFinished()
- + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
- + " mServedView=" + mServedView.get()
- + "}";
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- // Check that the call is initiated in the main thread of the current InputConnection
- // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
- // executed on this thread. Otherwise the messages are dispatched to the correct thread
- // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
- // reasons.
- if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper()
- == getLooper()) {
- ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId);
- }
- }
- }
-
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -1256,8 +1192,7 @@ public final class InputMethodManager {
mMainLooper = looper;
mH = new H(looper);
mDisplayId = displayId;
- mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this,
- null);
+ mIInputContext = new IInputConnectionWrapper(looper, mDummyInputConnection, this, null);
}
/**
@@ -1373,7 +1308,9 @@ public final class InputMethodManager {
* @return {@link List} of {@link InputMethodInfo}.
* @hide
*/
+ @TestApi
@RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+ @NonNull
public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
try {
final Completable.InputMethodInfoList value = Completable.createInputMethodInfoList();
@@ -1544,6 +1481,15 @@ public final class InputMethodManager {
}
/**
+ * Return {@code true} if the input method is suppressing system spell checker.
+ */
+ public boolean isInputMethodSuppressingSpellChecker() {
+ synchronized (mH) {
+ return mIsInputMethodSuppressingSpellChecker;
+ }
+ }
+
+ /**
* Reset all of the state associated with being bound to an input method.
*/
void clearBindingLocked() {
@@ -1587,6 +1533,7 @@ public final class InputMethodManager {
@UnsupportedAppUsage
void finishInputLocked() {
mActivityViewToScreenMatrix = null;
+ mIsInputMethodSuppressingSpellChecker = false;
setNextServedViewLocked(null);
if (getServedViewLocked() != null) {
if (DEBUG) {
@@ -2063,7 +2010,7 @@ public final class InputMethodManager {
mServedInputConnectionWrapper.deactivate();
mServedInputConnectionWrapper = null;
}
- ControlledInputConnectionWrapper servedContext;
+ IInputConnectionWrapper servedContext;
final int missingMethodFlags;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
@@ -2080,7 +2027,7 @@ public final class InputMethodManager {
} else {
icHandler = ic.getHandler();
}
- servedContext = new ControlledInputConnectionWrapper(
+ servedContext = new IInputConnectionWrapper(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
} else {
servedContext = null;
@@ -2111,6 +2058,7 @@ public final class InputMethodManager {
return false;
}
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+ mIsInputMethodSuppressingSpellChecker = res.isInputMethodSuppressingSpellChecker;
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 6d5077a99c62..ef5004536354 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -145,7 +145,7 @@ public interface TextClassifier {
@StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_EDITTEXT, WIDGET_TYPE_UNSELECTABLE_TEXTVIEW,
WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW,
WIDGET_TYPE_CUSTOM_EDITTEXT, WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW,
- WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_UNKNOWN})
+ WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_CLIPBOARD, WIDGET_TYPE_UNKNOWN })
@interface WidgetType {}
/** The widget involved in the text classification context is a standard
@@ -172,6 +172,8 @@ public interface TextClassifier {
String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
/** The widget involved in the text classification context is a notification */
String WIDGET_TYPE_NOTIFICATION = "notification";
+ /** The text classification context is for use with the system clipboard. */
+ String WIDGET_TYPE_CLIPBOARD = "clipboard";
/** The widget involved in the text classification context is of an unknown/unspecified type. */
String WIDGET_TYPE_UNKNOWN = "unknown";
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 35d84458bd35..ba58b6525a6d 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -25,6 +25,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -176,6 +177,11 @@ public class SpellCheckerSession {
* @param suggestionsLimit the maximum number of suggestions that will be returned
*/
public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) {
+ final InputMethodManager imm = mTextServicesManager.getInputMethodManager();
+ if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+ handleOnGetSentenceSuggestionsMultiple(new SentenceSuggestionsInfo[0]);
+ return;
+ }
mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
textInfos, suggestionsLimit);
}
@@ -204,6 +210,11 @@ public class SpellCheckerSession {
if (DBG) {
Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
}
+ final InputMethodManager imm = mTextServicesManager.getInputMethodManager();
+ if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+ handleOnGetSuggestionsMultiple(new SuggestionsInfo[0]);
+ return;
+ }
mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
textInfos, suggestionsLimit, sequentialWords);
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 996757da0641..6fb01a309402 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -30,6 +30,7 @@ import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -88,10 +89,15 @@ public final class TextServicesManager {
@UserIdInt
private final int mUserId;
- private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException {
+ @Nullable
+ private final InputMethodManager mInputMethodManager;
+
+ private TextServicesManager(@UserIdInt int userId,
+ @Nullable InputMethodManager inputMethodManager) throws ServiceNotFoundException {
mService = ITextServicesManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
mUserId = userId;
+ mInputMethodManager = inputMethodManager;
}
/**
@@ -105,7 +111,8 @@ public final class TextServicesManager {
@NonNull
public static TextServicesManager createInstance(@NonNull Context context)
throws ServiceNotFoundException {
- return new TextServicesManager(context.getUserId());
+ return new TextServicesManager(context.getUserId(), context.getSystemService(
+ InputMethodManager.class));
}
/**
@@ -118,7 +125,7 @@ public final class TextServicesManager {
synchronized (TextServicesManager.class) {
if (sInstance == null) {
try {
- sInstance = new TextServicesManager(UserHandle.myUserId());
+ sInstance = new TextServicesManager(UserHandle.myUserId(), null);
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
@@ -127,6 +134,12 @@ public final class TextServicesManager {
}
}
+ /** @hide */
+ @Nullable
+ public InputMethodManager getInputMethodManager() {
+ return mInputMethodManager;
+ }
+
/**
* Returns the language component of a given locale string.
*/
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index 7f6c4b474d3a..d347f31eb934 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -17,6 +17,7 @@
package android.view.translation;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
@@ -40,4 +41,7 @@ oneway interface ITranslationManager {
void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
int userId);
+
+ void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
+ void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index cf3358b0dfbb..9f90b3bf1322 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.Activity;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -54,6 +55,11 @@ import java.util.function.Consumer;
*/
public class UiTranslationController {
+ // TODO(b/182433547): remove Build.IS_DEBUGGABLE before ship. Enable the logging in debug build
+ // to help the debug during the development phase
+ public static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)
+ || Build.IS_DEBUGGABLE;
+
private static final String TAG = "UiTranslationController";
@NonNull
private final Activity mActivity;
@@ -93,6 +99,8 @@ public class UiTranslationController {
if (!mActivity.isResumed()) {
return;
}
+ Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
+ + (DEBUG ? ", views: " + views : ""));
switch (state) {
case STATE_UI_TRANSLATION_STARTED:
final Pair<TranslationSpec, TranslationSpec> specs =
@@ -149,8 +157,69 @@ public class UiTranslationController {
translator.dump(outerPrefix, pw);
pw.println();
}
+ synchronized (mLock) {
+ final int viewSize = mViews.size();
+ pw.print(outerPrefix); pw.print("number views: "); pw.println(viewSize);
+ for (int i = 0; i < viewSize; i++) {
+ pw.print(outerPrefix); pw.print("#"); pw.println(i);
+ final AutofillId autofillId = mViews.keyAt(i);
+ final View view = mViews.valueAt(i).get();
+ pw.print(pfx); pw.print("autofillId: "); pw.println(autofillId);
+ pw.print(pfx); pw.print("view:"); pw.println(view);
+ }
+ }
+ // TODO(b/182433547): we will remove debug rom condition before S release then we change
+ // change this back to "DEBUG"
+ if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
+ dumpViewByTraversal(outerPrefix, pw);
+ }
+ }
+
+ private void dumpViewByTraversal(String outerPrefix, PrintWriter pw) {
+ final ArrayList<ViewRootImpl> roots =
+ WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+ pw.print(outerPrefix); pw.println("Dump views:");
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+ if (rootView instanceof ViewGroup) {
+ dumpChildren((ViewGroup) rootView, outerPrefix, pw);
+ } else {
+ dumpViewInfo(rootView, outerPrefix, pw);
+ }
+ }
+ }
+
+ private void dumpChildren(ViewGroup viewGroup, String outerPrefix, PrintWriter pw) {
+ final int childCount = viewGroup.getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ final View child = viewGroup.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ pw.print(outerPrefix); pw.println("Children: ");
+ pw.print(outerPrefix); pw.print(outerPrefix); pw.println(child);
+ dumpChildren((ViewGroup) child, outerPrefix, pw);
+ } else {
+ pw.print(outerPrefix); pw.println("End Children: ");
+ pw.print(outerPrefix); pw.print(outerPrefix); pw.print(child);
+ dumpViewInfo(child, outerPrefix, pw);
+ }
+ }
}
+ private void dumpViewInfo(View view, String outerPrefix, PrintWriter pw) {
+ final AutofillId autofillId = view.getAutofillId();
+ pw.print(outerPrefix); pw.print("autofillId: "); pw.print(autofillId);
+ // TODO: print TranslationTransformation
+ boolean isContainsView = false;
+ synchronized (mLock) {
+ final WeakReference<View> viewRef = mViews.get(autofillId);
+ if (viewRef != null && viewRef.get() != null) {
+ isContainsView = true;
+ }
+ }
+ pw.print(outerPrefix); pw.print("isContainsView: "); pw.println(isContainsView);
+ }
+
+
/**
* The method is used by {@link Translator}, it will be called when the translation is done. The
* translation result can be get from here.
@@ -171,6 +240,9 @@ public class UiTranslationController {
return;
}
final int resultCount = translatedResult.size();
+ if (DEBUG) {
+ Log.v(TAG, "onTranslationCompleted: receive " + resultCount + " responses.");
+ }
synchronized (mLock) {
for (int i = 0; i < resultCount; i++) {
final ViewTranslationResponse response = translatedResult.get(i);
@@ -180,7 +252,7 @@ public class UiTranslationController {
}
final View view = mViews.get(autofillId).get();
if (view == null) {
- Log.w(TAG, "onTranslationCompleted: the Veiew for autofill id " + autofillId
+ Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId
+ " may be gone.");
continue;
}
@@ -208,6 +280,10 @@ public class UiTranslationController {
@WorkerThread
private void sendTranslationRequest(Translator translator,
List<ViewTranslationRequest> requests) {
+ if (requests.size() == 0) {
+ Log.wtf(TAG, "No ViewTranslationRequest was collected.");
+ return;
+ }
final TranslationRequest request = new TranslationRequest.Builder()
.setViewTranslationRequests(requests)
.build();
@@ -233,7 +309,8 @@ public class UiTranslationController {
requests.add(request);
}
if (currentCount == (foundViews.size() - 1)) {
- Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request.");
+ Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
+ + " requests.");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
UiTranslationController.this, translator, requests));
@@ -287,6 +364,9 @@ public class UiTranslationController {
for (int i = 0; i < viewCounts; i++) {
final View view = views.valueAt(i).get();
if (view == null) {
+ if (DEBUG) {
+ Log.d(TAG, "View was gone for autofillid = " + views.keyAt(i));
+ }
continue;
}
action.accept(view);
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index a3a6a2e52138..852ffe8303b1 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -16,33 +16,49 @@
package android.view.translation;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.assist.ActivityId;
import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
+// TODO(b/178044703): Describe what UI Translation is.
/**
* The {@link UiTranslationManager} class provides ways for apps to use the ui translation
* function in framework.
- *
- * @hide
*/
-@SystemApi
public final class UiTranslationManager {
private static final String TAG = "UiTranslationManager";
/**
+ * The tag which uses for enabling debug log dump. To enable it, we can use command "adb shell
+ * setprop log.tag.UiTranslation DEBUG".
+ *
+ * @hide
+ */
+ public static final String LOG_TAG = "UiTranslation";
+
+ /**
* The state caller request to disable utranslation,, it is no longer need to ui translation.
*
* @hide
@@ -80,6 +96,14 @@ public final class UiTranslationManager {
public @interface UiTranslationState {
}
+ // Keys for the data transmitted in the internal UI Translation state callback.
+ /** @hide */
+ public static final String EXTRA_STATE = "state";
+ /** @hide */
+ public static final String EXTRA_SOURCE_LOCALE = "source_locale";
+ /** @hide */
+ public static final String EXTRA_TARGET_LOCALE = "target_locale";
+
@NonNull
private final Context mContext;
@@ -103,9 +127,12 @@ public final class UiTranslationManager {
* @param destSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
int taskId) {
@@ -133,8 +160,11 @@ public final class UiTranslationManager {
* @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
* @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
@NonNull ActivityId activityId) {
@@ -163,9 +193,12 @@ public final class UiTranslationManager {
* NOTE: Please use {@code finishTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void finishTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
@@ -183,8 +216,11 @@ public final class UiTranslationManager {
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void finishTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -204,9 +240,12 @@ public final class UiTranslationManager {
* NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void pauseTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
@@ -224,8 +263,11 @@ public final class UiTranslationManager {
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void pauseTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -245,9 +287,12 @@ public final class UiTranslationManager {
* NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void resumeTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
@@ -265,8 +310,11 @@ public final class UiTranslationManager {
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void resumeTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -278,4 +326,105 @@ public final class UiTranslationManager {
throw e.rethrowFromSystemServer();
}
}
+
+ // TODO(b/178044703): Fix the View API link when it becomes public.
+ /**
+ * Register for notifications of UI Translation state changes on the foreground activity. This
+ * is available to the owning application itself and also the current input method.
+ * <p>
+ * The application whose UI is being translated can use this to customize the UI Translation
+ * behavior in ways that aren't made easy by methods like
+ * View#onCreateTranslationRequest().
+ * <p>
+ * Input methods can use this to offer complementary features to UI Translation; for example,
+ * enabling outgoing message translation when the system is translating incoming messages in a
+ * communication app.
+ *
+ * @param callback the callback to register for receiving the state change
+ * notifications
+ */
+ public void registerUiTranslationStateCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull UiTranslationStateCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ synchronized (mCallbacks) {
+ if (mCallbacks.containsKey(callback)) {
+ Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;"
+ + " ignoring.");
+ return;
+ }
+ final IRemoteCallback remoteCallback =
+ new UiTranslationStateRemoteCallback(executor, callback);
+ try {
+ mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCallbacks.put(callback, remoteCallback);
+ }
+ }
+
+ /**
+ * Unregister {@code callback}.
+ *
+ * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)
+ */
+ public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) {
+ Objects.requireNonNull(callback);
+
+ synchronized (mCallbacks) {
+ final IRemoteCallback remoteCallback = mCallbacks.get(callback);
+ if (remoteCallback == null) {
+ Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring.");
+ return;
+ }
+ try {
+ mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCallbacks.remove(callback);
+ }
+ }
+
+ @NonNull
+ @GuardedBy("mCallbacks")
+ private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();
+
+ private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub {
+ private final Executor mExecutor;
+ private final UiTranslationStateCallback mCallback;
+
+ UiTranslationStateRemoteCallback(Executor executor,
+ UiTranslationStateCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void sendResult(Bundle bundle) {
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> onStateChange(bundle)));
+ }
+
+ private void onStateChange(Bundle bundle) {
+ int state = bundle.getInt(EXTRA_STATE);
+ switch (state) {
+ case STATE_UI_TRANSLATION_STARTED:
+ case STATE_UI_TRANSLATION_RESUMED:
+ mCallback.onStarted(
+ bundle.getString(EXTRA_SOURCE_LOCALE),
+ bundle.getString(EXTRA_TARGET_LOCALE));
+ break;
+ case STATE_UI_TRANSLATION_PAUSED:
+ mCallback.onPaused();
+ break;
+ case STATE_UI_TRANSLATION_FINISHED:
+ mCallback.onFinished();
+ break;
+ default:
+ Log.wtf(TAG, "Unexpected translation state:" + state);
+ }
+ }
+ }
}
diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java
new file mode 100644
index 000000000000..1946b703935d
--- /dev/null
+++ b/core/java/android/view/translation/UiTranslationStateCallback.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for listening to UI Translation state changes. See {@link
+ * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
+ */
+public interface UiTranslationStateCallback {
+
+ /**
+ * The system is requesting translation of the UI from {@code sourceLocale} to {@code
+ * targetLocale}.
+ * <p>
+ * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
+ * changed; or called again after {@link #onPaused()}.
+ */
+ void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);
+
+ /**
+ * The system is requesting that the application temporarily show the UI contents in their
+ * original language.
+ */
+ void onPaused();
+
+ /**
+ * The UI Translation session has ended.
+ */
+ void onFinished();
+}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 34fe51e82e8f..42d75353e5df 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -200,6 +200,8 @@ public class AnalogClock extends View {
mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
createClock();
+ a.recycle();
+
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index dc42ad583543..3c411126627f 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -20,6 +20,9 @@ import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
@@ -58,6 +61,29 @@ import java.lang.annotation.RetentionPolicy;
* {@link #draw(Canvas)} method.</p>
*/
public class EdgeEffect {
+ /**
+ * This sets the default value for {@link #setType(int)} to {@link #TYPE_STRETCH} instead
+ * of {@link #TYPE_GLOW}. The type can still be overridden by the theme, view attribute,
+ * or by calling {@link #setType(int)}.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BASE)
+ public static final long USE_STRETCH_EDGE_EFFECT_BY_DEFAULT = 171228096L;
+
+ /**
+ * This sets the default value for {@link #setType(int)} to {@link #TYPE_STRETCH} instead
+ * of {@link #TYPE_GLOW} for views that instantiate with
+ * {@link #EdgeEffect(Context, AttributeSet)}, indicating use of S+ EdgeEffect support. The
+ * type can still be overridden by the theme, view attribute, or by calling
+ * {@link #setType(int)}.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ public static final long USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED = 178807038L;
/**
* The default blend mode used by {@link EdgeEffect}.
@@ -76,13 +102,35 @@ public class EdgeEffect {
*/
public static final int TYPE_STRETCH = 1;
+ /**
+ * The velocity threshold before the spring animation is considered settled.
+ * The idea here is that velocity should be less than 1 pixel per frame (~16ms).
+ */
+ private static final double VELOCITY_THRESHOLD = 1.0 / 0.016;
+
+ /**
+ * The value threshold before the spring animation is considered close enough to
+ * the destination to be settled. This should be around 1 pixel.
+ */
+ private static final double VALUE_THRESHOLD = 1;
+
+ /**
+ * The natural frequency of the stretch spring.
+ */
+ private static final double NATURAL_FREQUENCY = 14.4222;
+
+ /**
+ * The damping ratio of the stretch spring.
+ */
+ private static final double DAMPING_RATIO = 0.875;
+
/** @hide */
@IntDef({TYPE_GLOW, TYPE_STRETCH})
@Retention(RetentionPolicy.SOURCE)
public @interface EdgeEffectType {
}
- private static final float DEFAULT_MAX_STRETCH_INTENSITY = 1.5f;
+ private static final float DEFAULT_MAX_STRETCH_INTENSITY = 0.08f;
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "EdgeEffect";
@@ -119,20 +167,20 @@ public class EdgeEffect {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mGlowScaleY;
private float mDistance;
+ private float mVelocity; // only for stretch animations
private float mGlowAlphaStart;
private float mGlowAlphaFinish;
private float mGlowScaleYStart;
private float mGlowScaleYFinish;
- private float mDistanceStart;
- private float mDistanceFinish;
private long mStartTime;
private float mDuration;
private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
+ private float mStretchDistanceFraction = 1f;
private float mStretchDistance = -1f;
- private final Interpolator mInterpolator;
+ private final Interpolator mInterpolator = new DecelerateInterpolator();
private static final int STATE_IDLE = 0;
private static final int STATE_PULL = 1;
@@ -166,7 +214,7 @@ public class EdgeEffect {
* @param context Context used to provide theming and resource information for the EdgeEffect
*/
public EdgeEffect(Context context) {
- this(context, null);
+ this(context, null, Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT));
}
/**
@@ -175,18 +223,26 @@ public class EdgeEffect {
* @param attrs The attributes of the XML tag that is inflating the view
*/
public EdgeEffect(@NonNull Context context, @Nullable AttributeSet attrs) {
- mPaint.setAntiAlias(true);
+ this(context, attrs,
+ Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
+ || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED));
+ }
+
+ private EdgeEffect(@NonNull Context context, @Nullable AttributeSet attrs,
+ boolean defaultStretch) {
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.EdgeEffect);
final int themeColor = a.getColor(
com.android.internal.R.styleable.EdgeEffect_colorEdgeEffect, 0xff666666);
mEdgeEffectType = a.getInt(
- com.android.internal.R.styleable.EdgeEffect_edgeEffectType, TYPE_GLOW);
+ com.android.internal.R.styleable.EdgeEffect_edgeEffectType,
+ defaultStretch ? TYPE_STRETCH : TYPE_GLOW);
a.recycle();
+
+ mPaint.setAntiAlias(true);
mPaint.setColor((themeColor & 0xffffff) | 0x33000000);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setBlendMode(DEFAULT_BLEND_MODE);
- mInterpolator = new DecelerateInterpolator();
}
/**
@@ -239,6 +295,8 @@ public class EdgeEffect {
*/
public void finish() {
mState = STATE_IDLE;
+ mDistance = 0;
+ mVelocity = 0;
}
/**
@@ -293,15 +351,17 @@ public class EdgeEffect {
mDuration = PULL_TIME;
mPullDistance += deltaDistance;
- mDistanceStart = mDistanceFinish = mDistance = Math.max(0f, mPullDistance);
-
- final float absdd = Math.abs(deltaDistance);
- mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
- mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+ mDistance = Math.max(0f, mPullDistance);
+ mVelocity = 0;
if (mPullDistance == 0) {
mGlowScaleY = mGlowScaleYStart = 0;
+ mGlowAlpha = mGlowAlphaStart = 0;
} else {
+ final float absdd = Math.abs(deltaDistance);
+ mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
+ mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+
final float scale = (float) (Math.max(0, 1 - 1 /
Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d);
@@ -387,11 +447,10 @@ public class EdgeEffect {
mState = STATE_RECEDE;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
- mDistanceStart = mDistance;
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
- mDistanceFinish = 0.f;
+ mVelocity = 0.f;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
@@ -408,30 +467,36 @@ public class EdgeEffect {
* @param velocity Velocity at impact in pixels per second.
*/
public void onAbsorb(int velocity) {
- mState = STATE_ABSORB;
- velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
-
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mDuration = 0.15f + (velocity * 0.02f);
-
- // The glow depends more on the velocity, and therefore starts out
- // nearly invisible.
- mGlowAlphaStart = GLOW_ALPHA_START;
- mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
- mDistanceStart = mDistance;
-
- // Growth for the size of the glow should be quadratic to properly
- // respond
- // to a user's scrolling speed. The faster the scrolling speed, the more
- // intense the effect should be for both the size and the saturation.
- mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
- // Alpha should change for the glow as well as size.
- mGlowAlphaFinish = Math.max(
- mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
- mTargetDisplacement = 0.5f;
-
- // Use glow values to estimate the absorption for stretch distance.
- mDistanceFinish = calculateDistanceFromGlowValues(mGlowScaleYFinish, mGlowAlphaFinish);
+ if (mEdgeEffectType == TYPE_STRETCH) {
+ mState = STATE_RECEDE;
+ mVelocity = velocity;
+ mDistance = 0;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ } else {
+ mState = STATE_ABSORB;
+ mVelocity = 0;
+ velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = 0.15f + (velocity * 0.02f);
+
+ // The glow depends more on the velocity, and therefore starts out
+ // nearly invisible.
+ mGlowAlphaStart = GLOW_ALPHA_START;
+ mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
+
+ // Growth for the size of the glow should be quadratic to properly
+ // respond
+ // to a user's scrolling speed. The faster the scrolling speed, the more
+ // intense the effect should be for both the size and the saturation.
+ mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2,
+ 1.f);
+ // Alpha should change for the glow as well as size.
+ mGlowAlphaFinish = Math.max(
+ mGlowAlphaStart,
+ Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
+ mTargetDisplacement = 0.5f;
+ }
}
/**
@@ -539,8 +604,8 @@ public class EdgeEffect {
canvas.drawCircle(centerX, centerY, mRadius, mPaint);
canvas.restoreToCount(count);
} else if (canvas instanceof RecordingCanvas) {
- if (mState != STATE_PULL) {
- update();
+ if (mState == STATE_RECEDE) {
+ updateSpring();
}
RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
if (mTmpMatrix == null) {
@@ -592,12 +657,12 @@ public class EdgeEffect {
// for now leverage placeholder logic if no stretch distance is provided to
// consume the displacement ratio times the minimum of the width or height
mStretchDistance > 0 ? mStretchDistance :
- (mDisplacement * Math.min(mWidth, mHeight))
+ (mStretchDistanceFraction * Math.max(mWidth, mHeight))
);
}
boolean oneLastFrame = false;
- if (mState == STATE_RECEDE && mDistance == 0) {
+ if (mState == STATE_RECEDE && mDistance == 0 && mVelocity == 0) {
mState = STATE_IDLE;
oneLastFrame = true;
}
@@ -634,7 +699,9 @@ public class EdgeEffect {
mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
- mDistance = mDistanceStart + (mDistanceFinish - mDistanceStart) * interp;
+ if (mState != STATE_PULL) {
+ mDistance = calculateDistanceFromGlowValues(mGlowScaleY, mGlowAlpha);
+ }
mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
if (t >= 1.f - EPSILON) {
@@ -646,12 +713,10 @@ public class EdgeEffect {
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
- mDistanceStart = mDistance;
// After absorb, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
- mDistanceFinish = 0.f;
break;
case STATE_PULL:
mState = STATE_PULL_DECAY;
@@ -660,12 +725,10 @@ public class EdgeEffect {
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
- mDistanceStart = mDistance;
// After pull, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
- mDistanceFinish = 0.f;
break;
case STATE_PULL_DECAY:
mState = STATE_RECEDE;
@@ -677,6 +740,35 @@ public class EdgeEffect {
}
}
+ private void updateSpring() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final float deltaT = (time - mStartTime) / 1000f; // Convert from millis to seconds
+ if (deltaT < 0.001f) {
+ return; // Must have at least 1 ms difference
+ }
+ final double mDampedFreq = NATURAL_FREQUENCY * Math.sqrt(1 - DAMPING_RATIO * DAMPING_RATIO);
+
+ // We're always underdamped, so we can use only those equations:
+ double cosCoeff = mDistance * mHeight;
+ double sinCoeff = (1 / mDampedFreq) * (DAMPING_RATIO * NATURAL_FREQUENCY
+ * mDistance * mHeight + mVelocity);
+ double distance = Math.pow(Math.E, -DAMPING_RATIO * NATURAL_FREQUENCY * deltaT)
+ * (cosCoeff * Math.cos(mDampedFreq * deltaT)
+ + sinCoeff * Math.sin(mDampedFreq * deltaT));
+ double velocity = distance * (-NATURAL_FREQUENCY) * DAMPING_RATIO
+ + Math.pow(Math.E, -DAMPING_RATIO * NATURAL_FREQUENCY * deltaT)
+ * (-mDampedFreq * cosCoeff * Math.sin(mDampedFreq * deltaT)
+ + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
+ mDistance = (float) distance / mHeight;
+ mVelocity = (float) velocity;
+ mStartTime = time;
+ if (isAtEquilibrium()) {
+ mState = STATE_IDLE;
+ mDistance = 0;
+ mVelocity = 0;
+ }
+ }
+
/**
* @return The estimated pull distance as calculated from mGlowScaleY.
*/
@@ -692,4 +784,14 @@ public class EdgeEffect {
}
return alpha / PULL_DISTANCE_ALPHA_GLOW_FACTOR;
}
+
+ /**
+ * @return true if the spring used for calculating the stretch animation is
+ * considered at rest or false if it is still animating.
+ */
+ private boolean isAtEquilibrium() {
+ double displacement = mDistance * mHeight; // in pixels
+ return Math.abs(mVelocity) < VELOCITY_THRESHOLD
+ && Math.abs(displacement) < VALUE_THRESHOLD;
+ }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 012352d0a0f5..238ce85622b5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -988,6 +988,12 @@ public class Editor {
if (mTextView.isTextEditable() && mTextView.isSuggestionsEnabled()
&& !(mTextView.isInExtractedMode())) {
+ final InputMethodManager imm = getInputMethodManager();
+ if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+ // Do not close mSpellChecker here as it may be reused when the current IME has been
+ // changed.
+ return;
+ }
if (mSpellChecker == null && createSpellChecker) {
mSpellChecker = new SpellChecker(mTextView);
}
@@ -2898,9 +2904,6 @@ public class Editor {
} finally {
mTextView.endBatchEdit();
mUndoInputFilter.freezeLastEdit();
- if (permissions != null) {
- permissions.release();
- }
}
}
@@ -3790,7 +3793,7 @@ public class Editor {
}
public SuggestionsPopupWindow() {
- mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+ mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr();
}
@Override
@@ -3957,7 +3960,7 @@ public class Editor {
}
if (updateSuggestions()) {
- mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+ mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr();
mTextView.setCursorVisible(false);
mIsShowingUp = true;
super.show();
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 718076b49f77..64570a8ad155 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -5,6 +5,8 @@ alanv@google.com
adamp@google.com
aurimas@google.com
siyamed@google.com
+mount@google.com
+njawad@google.com
per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
new file mode 100644
index 000000000000..d84330828bf7
--- /dev/null
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseIntArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.ColorResources;
+import android.widget.RemoteViews.InteractionHandler;
+import android.widget.RemoteViews.RemoteCollectionItems;
+
+import com.android.internal.R;
+
+import java.util.stream.IntStream;
+
+/**
+ * List {@link Adapter} backed by a {@link RemoteCollectionItems}.
+ *
+ * @hide
+ */
+class RemoteCollectionItemsAdapter extends BaseAdapter {
+
+ private final int mViewTypeCount;
+
+ private RemoteCollectionItems mItems;
+ private InteractionHandler mInteractionHandler;
+ private ColorResources mColorResources;
+
+ private SparseIntArray mLayoutIdToViewType;
+
+ RemoteCollectionItemsAdapter(
+ @NonNull RemoteCollectionItems items,
+ @NonNull InteractionHandler interactionHandler,
+ @NonNull ColorResources colorResources) {
+ // View type count can never increase after an adapter has been set on a ListView.
+ // Additionally, decreasing it could inhibit view recycling if the count were to back and
+ // forth between 3-2-3-2 for example. Therefore, the view type count, should be fixed for
+ // the lifetime of the adapter.
+ mViewTypeCount = items.getViewTypeCount();
+
+ mItems = items;
+ mInteractionHandler = interactionHandler;
+ mColorResources = colorResources;
+
+ initLayoutIdToViewType();
+ }
+
+ /**
+ * Updates the data for the adapter, allowing recycling of views. Note that if the view type
+ * count has increased, a new adapter should be created and set on the AdapterView instead of
+ * calling this method.
+ */
+ void setData(
+ @NonNull RemoteCollectionItems items,
+ @NonNull InteractionHandler interactionHandler,
+ @NonNull ColorResources colorResources) {
+ if (mViewTypeCount < items.getViewTypeCount()) {
+ throw new IllegalArgumentException(
+ "RemoteCollectionItemsAdapter cannot increase view type count after creation");
+ }
+
+ mItems = items;
+ mInteractionHandler = interactionHandler;
+ mColorResources = colorResources;
+
+ initLayoutIdToViewType();
+
+ notifyDataSetChanged();
+ }
+
+ private void initLayoutIdToViewType() {
+ SparseIntArray previousLayoutIdToViewType = mLayoutIdToViewType;
+ mLayoutIdToViewType = new SparseIntArray(mViewTypeCount);
+
+ int[] layoutIds = IntStream.range(0, mItems.getItemCount())
+ .map(position -> mItems.getItemView(position).getLayoutId())
+ .distinct()
+ .toArray();
+ if (layoutIds.length > mViewTypeCount) {
+ throw new IllegalArgumentException(
+ "Collection items uses " + layoutIds.length + " distinct layouts, which is "
+ + "more than view type count of " + mViewTypeCount);
+ }
+
+ // Tracks whether a layout id (by index, not value) has been assigned a view type.
+ boolean[] processedLayoutIdIndices = new boolean[layoutIds.length];
+ // Tracks whether a view type has been assigned to a layout id already.
+ boolean[] assignedViewTypes = new boolean[mViewTypeCount];
+
+ if (previousLayoutIdToViewType != null) {
+ for (int i = 0; i < layoutIds.length; i++) {
+ int layoutId = layoutIds[i];
+ // Copy over any previously used view types for layout ids in the collection to keep
+ // view types stable across data updates.
+ int previousViewType = previousLayoutIdToViewType.get(layoutId, -1);
+ // Skip this layout id if it wasn't assigned to a view type previously.
+ if (previousViewType < 0) continue;
+
+ mLayoutIdToViewType.put(layoutId, previousViewType);
+ processedLayoutIdIndices[i] = true;
+ assignedViewTypes[previousViewType] = true;
+ }
+ }
+
+ int lastViewType = -1;
+ for (int i = 0; i < layoutIds.length; i++) {
+ // If a view type has already been assigned to the layout id, skip it.
+ if (processedLayoutIdIndices[i]) continue;
+
+ int layoutId = layoutIds[i];
+ // If no view type is assigned for the layout id, choose the next possible value that
+ // isn't already assigned to a layout id. There is guaranteed to be some value available
+ // due to the prior validation logic that count(distinct layout ids) <= viewTypeCount.
+ int viewType = IntStream.range(lastViewType + 1, layoutIds.length)
+ .filter(type -> !assignedViewTypes[type])
+ .findFirst()
+ .orElseThrow(
+ () -> new IllegalStateException(
+ "RemoteCollectionItems has more distinct layout ids than its "
+ + "view type count"));
+ mLayoutIdToViewType.put(layoutId, viewType);
+ processedLayoutIdIndices[i] = true;
+ assignedViewTypes[viewType] = true;
+ lastViewType = viewType;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.getItemCount();
+ }
+
+ @Override
+ public RemoteViews getItem(int position) {
+ return mItems.getItemView(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mItems.getItemId(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mLayoutIdToViewType.get(mItems.getItemView(position).getLayoutId());
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return mViewTypeCount;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return mItems.hasStableIds();
+ }
+
+ @Nullable
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ if (position >= getCount()) return null;
+
+ RemoteViews item = mItems.getItemView(position);
+ item.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+ View reapplyView = getViewToReapply(convertView, item);
+
+ // Reapply the RemoteViews if we can.
+ if (reapplyView != null) {
+ try {
+ item.reapply(
+ parent.getContext(),
+ reapplyView,
+ mInteractionHandler,
+ null /* size */,
+ mColorResources);
+ return reapplyView;
+ } catch (RuntimeException e) {
+ // We can't reapply for some reason, we'll fallback to an apply and inflate a
+ // new view.
+ }
+ }
+
+ return item.apply(
+ parent.getContext(),
+ parent,
+ mInteractionHandler,
+ null /* size */,
+ mColorResources);
+ }
+
+ /** Returns {@code convertView} if it can be used to reapply {@code item}, or null otherwise. */
+ @Nullable
+ private static View getViewToReapply(@Nullable View convertView, @NonNull RemoteViews item) {
+ if (convertView == null) return null;
+
+ Object layoutIdTag = convertView.getTag(R.id.widget_frame);
+ if (!(layoutIdTag instanceof Integer)) return null;
+
+ return item.getLayoutId() == (Integer) layoutIdTag ? convertView : null;
+ }
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2328e589216b..0cedcea7b4d4 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -28,6 +28,7 @@ import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.StringRes;
import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -74,6 +75,7 @@ import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
+import android.util.LongArray;
import android.util.Pair;
import android.util.SizeF;
import android.util.SparseIntArray;
@@ -112,6 +114,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -120,6 +123,7 @@ import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* A class that describes a view hierarchy that can be displayed in
@@ -223,6 +227,7 @@ public class RemoteViews implements Parcelable, Filter {
private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30;
+ private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -898,6 +903,72 @@ public class RemoteViews implements Parcelable, Filter {
ArrayList<RemoteViews> list;
}
+ private static class SetRemoteCollectionItemListAdapterAction extends Action {
+ private final RemoteCollectionItems mItems;
+
+ SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) {
+ viewId = id;
+ mItems = items;
+ }
+
+ SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
+ viewId = parcel.readInt();
+ mItems = parcel.readTypedObject(RemoteCollectionItems.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeTypedObject(mItems, flags);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+ ColorResources colorResources) throws ActionException {
+ View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof AdapterView)) {
+ Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not "
+ + "an AdapterView (id: " + viewId + ")");
+ return;
+ }
+
+ AdapterView adapterView = (AdapterView) target;
+ Adapter adapter = adapterView.getAdapter();
+ // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
+ // count hasn't increased. Note that AbsListView allocates a fixed size array for view
+ // recycling in setAdapter, so we must call setAdapter again if the number of view types
+ // increases.
+ if (adapter instanceof RemoteCollectionItemsAdapter
+ && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
+ try {
+ ((RemoteCollectionItemsAdapter) adapter).setData(
+ mItems, handler, colorResources);
+ } catch (Throwable throwable) {
+ // setData should never failed with the validation in the items builder, but if
+ // it does, catch and rethrow.
+ throw new ActionException(throwable);
+ }
+ return;
+ }
+
+ try {
+ adapterView.setAdapter(
+ new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+ } catch (Throwable throwable) {
+ // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
+ // a type error.
+ throw new ActionException(throwable);
+ }
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG;
+ }
+ }
+
private class SetRemoteViewsAdapterIntent extends Action {
public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
this.viewId = id;
@@ -1993,22 +2064,79 @@ public class RemoteViews implements Parcelable, Filter {
mIsRoot = false;
}
+ private static boolean hasStableId(View view) {
+ Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
+ return tag != null;
+ }
+
+ private static int getStableId(View view) {
+ Integer id = (Integer) view.getTag(com.android.internal.R.id.remote_views_stable_id);
+ return id == null ? ViewGroupActionAdd.NO_ID : id;
+ }
+
+ private static void setStableId(View view, int stableId) {
+ view.setTagInternal(com.android.internal.R.id.remote_views_stable_id, stableId);
+ }
+
+ // Returns the next recyclable child of the view group, or -1 if there are none.
+ private static int getNextRecyclableChild(ViewGroup vg) {
+ Integer tag = (Integer) vg.getTag(com.android.internal.R.id.remote_views_next_child);
+ return tag == null ? -1 : tag;
+ }
+
+ private static int getViewLayoutId(View v) {
+ return (Integer) v.getTag(R.id.widget_frame);
+ }
+
+ private static void setNextRecyclableChild(ViewGroup vg, int nextChild, int numChildren) {
+ if (nextChild < 0 || nextChild >= numChildren) {
+ vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, -1);
+ } else {
+ vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, nextChild);
+ }
+ }
+
+ private void finalizeViewRecycling(ViewGroup root) {
+ // Remove any recyclable children that were not used. nextChild should either be -1 or point
+ // to the next recyclable child that hasn't been recycled.
+ int nextChild = getNextRecyclableChild(root);
+ if (nextChild >= 0 && nextChild < root.getChildCount()) {
+ root.removeViews(nextChild, root.getChildCount() - nextChild);
+ }
+ // Make sure on the next round, we don't try to recycle if removeAllViews is not called.
+ setNextRecyclableChild(root, -1, 0);
+ // Traverse the view tree.
+ for (int i = 0; i < root.getChildCount(); i++) {
+ View child = root.getChildAt(i);
+ if (child instanceof ViewGroup && !child.isRootNamespace()) {
+ finalizeViewRecycling((ViewGroup) child);
+ }
+ }
+ }
+
/**
* ViewGroup methods that are related to adding Views.
*/
private class ViewGroupActionAdd extends Action {
+ static final int NO_ID = -1;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private RemoteViews mNestedViews;
private int mIndex;
+ private int mStableId;
ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews) {
- this(viewId, nestedViews, -1 /* index */);
+ this(viewId, nestedViews, -1 /* index */, NO_ID /* nestedViewId */);
}
ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index) {
+ this(viewId, nestedViews, index, NO_ID /* nestedViewId */);
+ }
+
+ ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) {
this.viewId = viewId;
mNestedViews = nestedViews;
mIndex = index;
+ mStableId = stableId;
if (nestedViews != null) {
configureRemoteViewsAsChild(nestedViews);
}
@@ -2018,6 +2146,7 @@ public class RemoteViews implements Parcelable, Filter {
int depth, Map<Class, Object> classCookies) {
viewId = parcel.readInt();
mIndex = parcel.readInt();
+ mStableId = parcel.readInt();
mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
mNestedViews.addFlags(mApplyFlags);
}
@@ -2025,6 +2154,7 @@ public class RemoteViews implements Parcelable, Filter {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
dest.writeInt(mIndex);
+ dest.writeInt(mStableId);
mNestedViews.writeToParcel(dest, flags);
}
@@ -2033,6 +2163,17 @@ public class RemoteViews implements Parcelable, Filter {
return mNestedViews.hasSameAppInfo(parentInfo);
}
+ private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
+ for (int nextChild = getNextRecyclableChild(target); nextChild < target.getChildCount();
+ nextChild++) {
+ View child = target.getChildAt(nextChild);
+ if (getStableId(child) == mStableId) {
+ return nextChild;
+ }
+ }
+ return -1;
+ }
+
@Override
public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
ColorResources colorResources) {
@@ -2043,10 +2184,45 @@ public class RemoteViews implements Parcelable, Filter {
return;
}
+ // If removeAllViews was called, this returns the next potential recycled view.
+ // If there are no more views to recycle (or removeAllViews was not called), this
+ // will return -1.
+ final int nextChild = getNextRecyclableChild(target);
+ RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+ if (nextChild >= 0 && mStableId != NO_ID) {
+ // At that point, the views starting at index nextChild are the ones recyclable but
+ // not yet recycled. All views added on that round of application are placed before.
+ // Find the next view with the same stable id, or -1.
+ int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
+ if (recycledViewIndex >= 0) {
+ View child = target.getChildAt(recycledViewIndex);
+ if (rvToApply.canRecycleView(child)) {
+ if (nextChild < recycledViewIndex) {
+ target.removeViews(nextChild, recycledViewIndex - nextChild);
+ }
+ setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
+ rvToApply.reapply(context, child, handler, null /* size */, colorResources,
+ false /* topLevel */);
+ return;
+ }
+ // If we cannot recycle the views, we still remove all views in between to
+ // avoid weird behaviors and insert the new view in place of the old one.
+ target.removeViews(nextChild, recycledViewIndex - nextChild + 1);
+ }
+ }
+ // If we cannot recycle, insert the new view before the next recyclable child.
+
// Inflate nested views and add as children
- target.addView(
- mNestedViews.apply(context, target, handler, null /* size */, colorResources),
- mIndex);
+ View nestedView = rvToApply.apply(context, target, handler, null /* size */,
+ colorResources);
+ if (mStableId != NO_ID) {
+ setStableId(nestedView, mStableId);
+ }
+ target.addView(nestedView, mIndex >= 0 ? mIndex : nextChild);
+ if (nextChild >= 0) {
+ // If we are at the end, there is no reason to try to recycle anymore
+ setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
+ }
}
@Override
@@ -2063,24 +2239,91 @@ public class RemoteViews implements Parcelable, Filter {
// Inflate nested views and perform all the async tasks for the child remoteView.
final Context context = root.mRoot.getContext();
- final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg,
- null /* listener */, handler, null /* size */, colorResources);
+
+ // If removeAllViews was called, this returns the next potential recycled view.
+ // If there are no more views to recycle (or removeAllViews was not called), this
+ // will return -1.
+ final int nextChild = getNextRecyclableChild(targetVg);
+ if (nextChild >= 0 && mStableId != NO_ID) {
+ RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+ final int recycledViewIndex = target.findChildIndex(nextChild,
+ view -> getStableId(view) == mStableId);
+ if (recycledViewIndex >= 0) {
+ // At that point, the views starting at index nextChild are the ones
+ // recyclable but not yet recycled. All views added on that round of
+ // application are placed before.
+ ViewTree recycled = target.mChildren.get(recycledViewIndex);
+ // We can only recycle the view if the layout id is the same.
+ if (rvToApply.canRecycleView(recycled.mRoot)) {
+ if (recycledViewIndex > nextChild) {
+ target.removeChildren(nextChild, recycledViewIndex - nextChild);
+ }
+ setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
+ final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
+ context,
+ targetVg, null /* listener */, handler, null /* size */,
+ colorResources,
+ recycled.mRoot);
+ final ViewTree tree = reapplyTask.doInBackground();
+ if (tree == null) {
+ throw new ActionException(reapplyTask.mError);
+ }
+ return new RuntimeAction() {
+ @Override
+ public void apply(View root, ViewGroup rootParent,
+ InteractionHandler handler, ColorResources colorResources)
+ throws ActionException {
+ reapplyTask.onPostExecute(tree);
+ if (recycledViewIndex > nextChild) {
+ targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
+ }
+ }
+ };
+ }
+ // If the layout id is different, still remove the children as if we recycled
+ // the view, to insert at the same place.
+ target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
+ return insertNewView(context, target, handler, colorResources,
+ () -> targetVg.removeViews(nextChild,
+ recycledViewIndex - nextChild + 1));
+
+ }
+ }
+ // If we cannot recycle, simply add the view at the same available slot.
+ return insertNewView(context, target, handler, colorResources, () -> {});
+ }
+
+ private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
+ ColorResources colorResources, Runnable finalizeAction) {
+ ViewGroup targetVg = (ViewGroup) target.mRoot;
+ int nextChild = getNextRecyclableChild(targetVg);
+ final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
+ null /* listener */, handler, null /* size */, colorResources,
+ null /* result */);
final ViewTree tree = task.doInBackground();
if (tree == null) {
throw new ActionException(task.mError);
}
+ if (mStableId != NO_ID) {
+ setStableId(task.mResult, mStableId);
+ }
// Update the global view tree, so that next call to findViewTreeById
// goes through the subtree as well.
- target.addChild(tree, mIndex);
+ final int insertIndex = mIndex >= 0 ? mIndex : nextChild;
+ target.addChild(tree, insertIndex);
+ if (nextChild >= 0) {
+ setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
+ }
return new RuntimeAction() {
@Override
public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
ColorResources colorResources) throws ActionException {
task.onPostExecute(tree);
- targetVg.addView(task.mResult, mIndex);
+ finalizeAction.run();
+ targetVg.addView(task.mResult, insertIndex);
}
};
}
@@ -2148,7 +2391,14 @@ public class RemoteViews implements Parcelable, Filter {
}
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
- target.removeAllViews();
+ // Remote any view without a stable id
+ for (int i = target.getChildCount() - 1; i >= 0; i--) {
+ if (!hasStableId(target.getChildAt(i))) {
+ target.removeViewAt(i);
+ }
+ }
+ // In the end, only children with a stable id (i.e. recyclable) are left.
+ setNextRecyclableChild(target, 0, target.getChildCount());
return;
}
@@ -2170,8 +2420,8 @@ public class RemoteViews implements Parcelable, Filter {
final ViewGroup targetVg = (ViewGroup) target.mRoot;
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
- // Clear all children when there's no excepted view
- target.mChildren = null;
+ target.mChildren.removeIf(childTree -> !hasStableId(childTree.mRoot));
+ setNextRecyclableChild(targetVg, 0, target.mChildren.size());
} else {
// Remove just the children which don't match the excepted view
target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
@@ -2184,7 +2434,11 @@ public class RemoteViews implements Parcelable, Filter {
public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
ColorResources colorResources) throws ActionException {
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
- targetVg.removeAllViews();
+ for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
+ if (!hasStableId(targetVg.getChildAt(i))) {
+ targetVg.removeViewAt(i);
+ }
+ }
return;
}
@@ -3084,6 +3338,7 @@ public class RemoteViews implements Parcelable, Filter {
}
mApplication = portrait.mApplication;
mLayoutId = portrait.mLayoutId;
+ mViewId = portrait.mViewId;
mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
mLandscape = landscape;
@@ -3136,6 +3391,7 @@ public class RemoteViews implements Parcelable, Filter {
RemoteViews smallestView = findSmallestRemoteView();
mApplication = smallestView.mApplication;
mLayoutId = smallestView.mLayoutId;
+ mViewId = smallestView.mViewId;
mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
}
@@ -3253,6 +3509,7 @@ public class RemoteViews implements Parcelable, Filter {
ApplicationInfo.CREATOR.createFromParcel(parcel);
mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
mLayoutId = parcel.readInt();
+ mViewId = parcel.readInt();
mLightBackgroundLayoutId = parcel.readInt();
readActionsFromParcel(parcel, depth);
@@ -3273,6 +3530,7 @@ public class RemoteViews implements Parcelable, Filter {
RemoteViews smallestView = findSmallestRemoteView();
mApplication = smallestView.mApplication;
mLayoutId = smallestView.mLayoutId;
+ mViewId = smallestView.mViewId;
mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
} else {
// MODE_HAS_LANDSCAPE_AND_PORTRAIT
@@ -3281,6 +3539,7 @@ public class RemoteViews implements Parcelable, Filter {
mClassCookies);
mApplication = mPortrait.mApplication;
mLayoutId = mPortrait.mLayoutId;
+ mViewId = mPortrait.mViewId;
mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
}
mApplyFlags = parcel.readInt();
@@ -3354,6 +3613,8 @@ public class RemoteViews implements Parcelable, Filter {
return new SetOnCheckedChangeResponse(parcel);
case NIGHT_MODE_REFLECTION_ACTION_TAG:
return new NightModeReflectionAction(parcel);
+ case SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG:
+ return new SetRemoteCollectionItemListAdapterAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3458,6 +3719,30 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the given
+ * {@link RemoteViews}. If the {@link RemoteViews} may be re-inflated or updated,
+ * {@link #removeAllViews(int)} must be called on the same {@code viewId
+ * } before the first call to this method for the behavior of this method to be predictable.
+ *
+ * The {@code stableId} will be used to identify a potential view to recycled when the remote
+ * view is inflated. Views can be re-used if inserted in the same order, potentially with
+ * some views appearing / disappearing. To be recycled the view must not change the layout
+ * used to inflate it or its view id (see {@link RemoteViews#setViewId}).
+ *
+ * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
+ * are not reset, so what was applied in previous round will have an effect. As a view may be
+ * re-created at any time by the host, the RemoteViews should not rely on keeping information
+ * from previous applications and always re-set all the properties they need.
+ *
+ * @param viewId The id of the parent {@link ViewGroup} to add child into.
+ * @param nestedView {@link RemoteViews} that describes the child.
+ * @param stableId An id that is stable across different versions of RemoteViews.
+ */
+ public void addStableView(@IdRes int viewId, @NonNull RemoteViews nestedView, int stableId) {
+ addAction(new ViewGroupActionAdd(viewId, nestedView, -1 /* index */, stableId));
+ }
+
+ /**
* Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
* given {@link RemoteViews}.
*
@@ -4003,6 +4288,25 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
+ * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
+ * This is a simpler but less flexible approach to populating collection widgets. Its use is
+ * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
+ * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
+ * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
+ * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
+ *
+ * This API is supported in the compatibility library for previous API levels, see
+ * RemoteViewsCompat.
+ *
+ * @param viewId The id of the {@link AdapterView}.
+ * @param items The items to display in the {@link AdapterView}.
+ */
+ public void setRemoteAdapter(@IdRes int viewId, @NonNull RemoteCollectionItems items) {
+ addAction(new SetRemoteCollectionItemListAdapterAction(viewId, items));
+ }
+
+ /**
* Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
*
* @param viewId The id of the view to change
@@ -4123,8 +4427,7 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Sets an OutlineProvider on the view whose corner radius is a dimension calculated using
- * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. This outline may change shape
- * during system transitions.
+ * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}.
*
* <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
* Setting margins in pixels will behave poorly when the RemoteViews object is used on a
@@ -4137,7 +4440,7 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Sets an OutlineProvider on the view whose corner radius is a dimension resource with
- * {@code resId}. This outline may change shape during system transitions.
+ * {@code resId}.
*/
public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId));
@@ -4191,7 +4494,8 @@ public class RemoteViews implements Parcelable, Filter {
* Call a method taking one int, a size in pixels, on a view in the layout for this
* RemoteViews.
*
- * The dimension will be resolved from the resources at the time of inflation.
+ * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4223,7 +4527,8 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Call a method taking one int, a color, on a view in the layout for this RemoteViews.
*
- * The ColorStateList will be resolved from the resources at the time of inflation.
+ * The Color will be resolved from the resources at the time the {@link RemoteViews} is (re-)
+ * applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4300,7 +4605,8 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
*
- * The ColorStateList will be resolved from the resources at the time of inflation.
+ * The ColorStateList will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4339,7 +4645,8 @@ public class RemoteViews implements Parcelable, Filter {
* Call a method taking one float, a size in pixels, on a view in the layout for this
* RemoteViews.
*
- * The dimension will be resolved from the resources at the time of inflation.
+ * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4355,7 +4662,8 @@ public class RemoteViews implements Parcelable, Filter {
* Call a method taking one float, a size in pixels, on a view in the layout for this
* RemoteViews.
*
- * The dimension will be resolved from the specified dimension at the time of inflation.
+ * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4417,7 +4725,8 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Call a method taking one CharSequence on a view in the layout for this RemoteViews.
*
- * The CharSequence will be resolved from the resources at the time of inflation.
+ * The CharSequence will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4808,6 +5117,7 @@ public class RemoteViews implements Parcelable, Filter {
View v = inflater.inflate(rv.getLayoutId(), parent, false);
if (mViewId != View.NO_ID) {
v.setId(mViewId);
+ v.setTagInternal(R.id.remote_views_override_id, mViewId);
}
v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
return v;
@@ -4870,23 +5180,24 @@ public class RemoteViews implements Parcelable, Filter {
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
SizeF size) {
- return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
- .startTaskOnExecutor(executor);
+ return applyAsync(context, parent, executor, listener, handler, size,
+ null /* themeColors */);
}
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
- return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
- .startTaskOnExecutor(executor);
+ return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
+ handler, colorResources, null /* result */,
+ true /* topLevel */).startTaskOnExecutor(executor);
}
- private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
+ private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
- ColorResources colorResources) {
+ ColorResources colorResources, View result) {
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, null /* result */);
+ handler, colorResources, result, false /* topLevel */);
}
private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -4898,6 +5209,12 @@ public class RemoteViews implements Parcelable, Filter {
final OnViewAppliedListener mListener;
final InteractionHandler mHandler;
final ColorResources mColorResources;
+ /**
+ * Whether the remote view is the top-level one (i.e. not within an action).
+ *
+ * This is only used if the result is specified (i.e. the view is being recycled).
+ */
+ final boolean mTopLevel;
private View mResult;
private ViewTree mTree;
@@ -4906,13 +5223,15 @@ public class RemoteViews implements Parcelable, Filter {
private AsyncApplyTask(
RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
- InteractionHandler handler, ColorResources colorResources, View result) {
+ InteractionHandler handler, ColorResources colorResources,
+ View result, boolean topLevel) {
mRV = rv;
mParent = parent;
mContext = context;
mListener = listener;
mColorResources = colorResources;
mHandler = handler;
+ mTopLevel = topLevel;
mResult = result;
}
@@ -4959,6 +5278,10 @@ public class RemoteViews implements Parcelable, Filter {
a.apply(viewTree.mRoot, mParent, handler, mColorResources);
}
}
+ // If the parent of the view is has is a root, resolve the recycling.
+ if (mTopLevel && mResult instanceof ViewGroup) {
+ finalizeViewRecycling((ViewGroup) mResult);
+ }
} catch (Exception e) {
mError = e;
}
@@ -5011,6 +5334,21 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
+ reapply(context, v, handler, size, colorResources, true);
+ }
+
+ /** @hide */
+ public boolean canRecycleView(View v) {
+ Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id);
+ int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag;
+ return (Integer) v.getTag(R.id.widget_frame) == getLayoutId() && mViewId == overrideId;
+ }
+
+ // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+ // should set it to false.
+ private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
+ ColorResources colorResources, boolean topLevel) {
+
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation or size, is
@@ -5018,13 +5356,18 @@ public class RemoteViews implements Parcelable, Filter {
// (orientation or size), we throw an exception, since the layouts may be completely
// unrelated.
if (hasMultipleLayouts()) {
- if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
+ if (!rvToApply.canRecycleView(v)) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
}
rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
+
+ // If the parent of the view is has is a root, resolve the recycling.
+ if (topLevel && v instanceof ViewGroup) {
+ finalizeViewRecycling((ViewGroup) v);
+ }
}
/**
@@ -5068,8 +5411,8 @@ public class RemoteViews implements Parcelable, Filter {
}
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
- context, listener, handler, colorResources, v).startTaskOnExecutor(
- executor);
+ context, listener, handler, colorResources, v, true /* topLevel */)
+ .startTaskOnExecutor(executor);
}
private void performApply(View v, ViewGroup parent, InteractionHandler handler,
@@ -5282,6 +5625,7 @@ public class RemoteViews implements Parcelable, Filter {
mIdealSize.writeToParcel(dest, flags);
}
dest.writeInt(mLayoutId);
+ dest.writeInt(mViewId);
dest.writeInt(mLightBackgroundLayoutId);
writeActionsToParcel(dest);
} else if (hasSizedRemoteViews()) {
@@ -5470,6 +5814,14 @@ public class RemoteViews implements Parcelable, Filter {
mChildren.add(index, child);
}
+ public void removeChildren(int start, int count) {
+ if (mChildren != null) {
+ for (int i = 0; i < count; i++) {
+ mChildren.remove(start);
+ }
+ }
+ }
+
private void addViewChild(View v) {
// ViewTree only contains Views which can be found using findViewById.
// If isRootNamespace is true, this view is skipped.
@@ -5500,6 +5852,28 @@ public class RemoteViews implements Parcelable, Filter {
}
}
}
+
+ /** Find the first child for which the condition is true and return its index. */
+ public int findChildIndex(Predicate<View> condition) {
+ return findChildIndex(0, condition);
+ }
+
+ /**
+ * Find the first child, starting at {@code startIndex}, for which the condition is true and
+ * return its index.
+ */
+ public int findChildIndex(int startIndex, Predicate<View> condition) {
+ if (mChildren == null) {
+ return -1;
+ }
+
+ for (int i = startIndex; i < mChildren.size(); i++) {
+ if (condition.test(mChildren.get(i).mRoot)) {
+ return i;
+ }
+ }
+ return -1;
+ }
}
/**
@@ -5757,10 +6131,207 @@ public class RemoteViews implements Parcelable, Filter {
return true;
}
+ /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
+ public static final class RemoteCollectionItems implements Parcelable {
+ private final long[] mIds;
+ private final RemoteViews[] mViews;
+ private final boolean mHasStableIds;
+ private final int mViewTypeCount;
+
+ RemoteCollectionItems(
+ long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
+ mIds = ids;
+ mViews = views;
+ mHasStableIds = hasStableIds;
+ mViewTypeCount = viewTypeCount;
+ if (ids.length != views.length) {
+ throw new IllegalArgumentException(
+ "RemoteCollectionItems has different number of ids and views");
+ }
+ if (viewTypeCount < 1) {
+ throw new IllegalArgumentException("View type count must be >= 1");
+ }
+ int layoutIdCount = (int) Arrays.stream(views)
+ .mapToInt(RemoteViews::getLayoutId)
+ .distinct()
+ .count();
+ if (layoutIdCount > viewTypeCount) {
+ throw new IllegalArgumentException(
+ "View type count is set to " + viewTypeCount + ", but the collection "
+ + "contains " + layoutIdCount + " different layout ids");
+ }
+ }
+
+ RemoteCollectionItems(Parcel in) {
+ int length = in.readInt();
+ mIds = new long[length];
+ in.readLongArray(mIds);
+ mViews = new RemoteViews[length];
+ in.readTypedArray(mViews, RemoteViews.CREATOR);
+ mHasStableIds = in.readBoolean();
+ mViewTypeCount = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mIds.length);
+ dest.writeLongArray(mIds);
+ dest.writeTypedArray(mViews, flags);
+ dest.writeBoolean(mHasStableIds);
+ dest.writeInt(mViewTypeCount);
+ }
+
+ /**
+ * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id
+ * should be considered meaningful across collection updates.
+ *
+ * @return Id for the position.
+ */
+ public long getItemId(int position) {
+ return mIds[position];
+ }
+
+ /**
+ * Returns the {@link RemoteViews} to display at {@code position}.
+ *
+ * @return RemoteViews for the position.
+ */
+ @NonNull
+ public RemoteViews getItemView(int position) {
+ return mViews[position];
+ }
+
+ /**
+ * Returns the number of elements in the collection.
+ *
+ * @return Count of items.
+ */
+ public int getItemCount() {
+ return mIds.length;
+ }
+
+ /**
+ * Returns the view type count for the collection when used in an adapter
+ *
+ * @return Count of view types for the collection when used in an adapter.
+ * @see android.widget.Adapter#getViewTypeCount()
+ */
+ public int getViewTypeCount() {
+ return mViewTypeCount;
+ }
+
+ /**
+ * Indicates whether the item ids are stable across changes to the underlying data.
+ *
+ * @return True if the same id always refers to the same object.
+ * @see android.widget.Adapter#hasStableIds()
+ */
+ public boolean hasStableIds() {
+ return mHasStableIds;
+ }
+
+ @NonNull
+ public static final Creator<RemoteCollectionItems> CREATOR =
+ new Creator<RemoteCollectionItems>() {
+ @NonNull
+ @Override
+ public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
+ return new RemoteCollectionItems(source);
+ }
+
+ @NonNull
+ @Override
+ public RemoteCollectionItems[] newArray(int size) {
+ return new RemoteCollectionItems[size];
+ }
+ };
+
+ /** Builder class for {@link RemoteCollectionItems} objects.*/
+ public static final class Builder {
+ private final LongArray mIds = new LongArray();
+ private final List<RemoteViews> mViews = new ArrayList<>();
+ private boolean mHasStableIds;
+ private int mViewTypeCount;
+
+ /**
+ * Adds a {@link RemoteViews} to the collection.
+ *
+ * @param id Id to associate with the row. Use {@link #setHasStableIds(boolean)} to
+ * indicate that ids are stable across changes to the collection.
+ * @param view RemoteViews to display for the row.
+ */
+ @NonNull
+ // Covered by getItemId, getItemView, getItemCount.
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder addItem(long id, @NonNull RemoteViews view) {
+ if (view == null) throw new NullPointerException();
+ if (view.hasMultipleLayouts()) {
+ throw new IllegalArgumentException(
+ "RemoteViews used in a RemoteCollectionItems cannot specify separate "
+ + "layouts for orientations or sizes.");
+ }
+ mIds.add(id);
+ mViews.add(view);
+ return this;
+ }
+
+ /**
+ * Sets whether the item ids are stable across changes to the underlying data.
+ *
+ * @see android.widget.Adapter#hasStableIds()
+ */
+ @NonNull
+ public Builder setHasStableIds(boolean hasStableIds) {
+ mHasStableIds = hasStableIds;
+ return this;
+ }
+
+ /**
+ * Sets the view type count for the collection when used in an adapter. This can be set
+ * to the maximum number of different layout ids that will be used by RemoteViews in
+ * this collection.
+ *
+ * If this value is not set, then a value will be inferred from the provided items. As
+ * a result, the adapter may need to be recreated when the list is updated with
+ * previously unseen RemoteViews layouts for new items.
+ *
+ * @see android.widget.Adapter#getViewTypeCount()
+ */
+ @NonNull
+ public Builder setViewTypeCount(int viewTypeCount) {
+ mViewTypeCount = viewTypeCount;
+ return this;
+ }
+
+ /** Creates the {@link RemoteCollectionItems} defined by this builder. */
+ @NonNull
+ public RemoteCollectionItems build() {
+ if (mViewTypeCount < 1) {
+ // If a view type count wasn't specified, set it to be the number of distinct
+ // layout ids used in the items.
+ mViewTypeCount = (int) mViews.stream()
+ .mapToInt(RemoteViews::getLayoutId)
+ .distinct()
+ .count();
+ }
+ return new RemoteCollectionItems(
+ mIds.toArray(),
+ mViews.toArray(new RemoteViews[0]),
+ mHasStableIds,
+ Math.max(mViewTypeCount, 1));
+ }
+ }
+ }
+
/**
- * Set the ID of the top-level view of the XML layout.
+ * Set the ID of the top-level view of the XML layout.
*
- * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
+ * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
*
* @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
* term of other RemoteViews (e.g. {@link #RemoteViews(RemoteViews, RemoteViews)}).
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index d59a415469b6..2464b4af23eb 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -26,6 +26,7 @@ import android.text.style.SpellCheckSpan;
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.util.LruCache;
+import android.util.Range;
import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SpellCheckerSession;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
@@ -62,7 +63,8 @@ public class SpellChecker implements SpellCheckerSessionListener {
// Pause between each spell check to keep the UI smooth
private final static int SPELL_PAUSE_DURATION = 400; // milliseconds
- private static final int MIN_SENTENCE_LENGTH = 50;
+ // The maximum length of sentence.
+ private static final int MAX_SENTENCE_LENGTH = WORD_ITERATOR_INTERVAL;
private static final int USE_SPAN_RANGE = -1;
@@ -89,7 +91,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
// Shared by all SpellParsers. Cannot be shared with TextView since it may be used
// concurrently due to the asynchronous nature of onGetSuggestions.
- private WordIterator mWordIterator;
+ private SentenceIteratorWrapper mSentenceIterator;
@Nullable
private TextServicesManager mTextServicesManager;
@@ -151,8 +153,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
resetSession();
if (locale != null) {
- // Change SpellParsers' wordIterator locale
- mWordIterator = new WordIterator(locale);
+ // Change SpellParsers' sentenceIterator locale
+ mSentenceIterator = new SentenceIteratorWrapper(
+ BreakIterator.getSentenceInstance(locale));
}
// This class is the listener for locale change: warn other locale-aware objects
@@ -306,22 +309,30 @@ public class SpellChecker implements SpellCheckerSessionListener {
final int start = editable.getSpanStart(spellCheckSpan);
final int end = editable.getSpanEnd(spellCheckSpan);
- // Do not check this word if the user is currently editing it
- final boolean isEditing;
+ // Check the span if any of following conditions is met:
+ // - the user is not currently editing it
+ // - or `forceCheckWhenEditingWord` is true.
+ final boolean isNotEditing;
// Defer spell check when typing a word ending with a punctuation like an apostrophe
// which could end up being a mid-word punctuation.
if (selectionStart == end + 1
&& WordIterator.isMidWordPunctuation(
mCurrentLocale, Character.codePointBefore(editable, end + 1))) {
- isEditing = false;
- } else {
+ isNotEditing = false;
+ } else if (selectionEnd <= start || selectionStart > end) {
// Allow the overlap of the cursor and the first boundary of the spell check span
// no to skip the spell check of the following word because the
// following word will never be spell-checked even if the user finishes composing
- isEditing = selectionEnd <= start || selectionStart > end;
+ isNotEditing = true;
+ } else {
+ // When cursor is at the end of spell check span, allow spell check if the
+ // character before cursor is a separator.
+ isNotEditing = selectionStart == end
+ && selectionStart > 0
+ && isSeparator(Character.codePointBefore(editable, selectionStart));
}
- if (start >= 0 && end > start && (forceCheckWhenEditingWord || isEditing)) {
+ if (start >= 0 && end > start && (forceCheckWhenEditingWord || isNotEditing)) {
spellCheckSpan.setSpellCheckInProgress(true);
final TextInfo textInfo = new TextInfo(editable, start, end, mCookie, mIds[i]);
textInfos[textInfosCount++] = textInfo;
@@ -346,6 +357,19 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
}
+ private static boolean isSeparator(int codepoint) {
+ final int type = Character.getType(codepoint);
+ return ((1 << type) & ((1 << Character.SPACE_SEPARATOR)
+ | (1 << Character.LINE_SEPARATOR)
+ | (1 << Character.PARAGRAPH_SEPARATOR)
+ | (1 << Character.DASH_PUNCTUATION)
+ | (1 << Character.END_PUNCTUATION)
+ | (1 << Character.FINAL_QUOTE_PUNCTUATION)
+ | (1 << Character.INITIAL_QUOTE_PUNCTUATION)
+ | (1 << Character.START_PUNCTUATION)
+ | (1 << Character.OTHER_PUNCTUATION))) != 0;
+ }
+
private SpellCheckSpan onGetSuggestionsInternal(
SuggestionsInfo suggestionsInfo, int offset, int length) {
if (suggestionsInfo == null || suggestionsInfo.getCookie() != mCookie) {
@@ -534,6 +558,60 @@ public class SpellChecker implements SpellCheckerSessionListener {
mTextView.invalidateRegion(start, end, false /* No cursor involved */);
}
+ /**
+ * A wrapper of sentence iterator which only processes the specified window of the given text.
+ */
+ private static class SentenceIteratorWrapper {
+ private BreakIterator mSentenceIterator;
+ private int mStartOffset;
+ private int mEndOffset;
+
+ SentenceIteratorWrapper(BreakIterator sentenceIterator) {
+ mSentenceIterator = sentenceIterator;
+ }
+
+ /**
+ * Set the char sequence and the text window to process.
+ */
+ public void setCharSequence(CharSequence sequence, int start, int end) {
+ mStartOffset = Math.max(0, start);
+ mEndOffset = Math.min(end, sequence.length());
+ mSentenceIterator.setText(sequence.subSequence(mStartOffset, mEndOffset).toString());
+ }
+
+ /**
+ * See {@link BreakIterator#preceding(int)}
+ */
+ public int preceding(int offset) {
+ if (offset < mStartOffset) {
+ return BreakIterator.DONE;
+ }
+ int result = mSentenceIterator.preceding(offset - mStartOffset);
+ return result == BreakIterator.DONE ? BreakIterator.DONE : result + mStartOffset;
+ }
+
+ /**
+ * See {@link BreakIterator#following(int)}
+ */
+ public int following(int offset) {
+ if (offset > mEndOffset) {
+ return BreakIterator.DONE;
+ }
+ int result = mSentenceIterator.following(offset - mStartOffset);
+ return result == BreakIterator.DONE ? BreakIterator.DONE : result + mStartOffset;
+ }
+
+ /**
+ * See {@link BreakIterator#isBoundary(int)}
+ */
+ public boolean isBoundary(int offset) {
+ if (offset < mStartOffset || offset > mEndOffset) {
+ return false;
+ }
+ return mSentenceIterator.isBoundary(offset - mStartOffset);
+ }
+ }
+
private class SpellParser {
private Object mRange = new Object();
@@ -582,27 +660,15 @@ public class SpellChecker implements SpellCheckerSessionListener {
public void parse() {
Editable editable = (Editable) mTextView.getText();
- // Iterate over the newly added text and schedule new SpellCheckSpans
- final int start = Math.max(
- 0, editable.getSpanStart(mRange) - MIN_SENTENCE_LENGTH);
-
- final int end = editable.getSpanEnd(mRange);
-
- int wordIteratorWindowEnd = Math.min(end, start + WORD_ITERATOR_INTERVAL);
- mWordIterator.setCharSequence(editable, start, wordIteratorWindowEnd);
-
- // Move back to the beginning of the current word, if any
- int wordStart = mWordIterator.preceding(start);
- int wordEnd;
- if (wordStart == BreakIterator.DONE) {
- wordEnd = mWordIterator.following(start);
- if (wordEnd != BreakIterator.DONE) {
- wordStart = mWordIterator.getBeginning(wordEnd);
- }
- } else {
- wordEnd = mWordIterator.getEnd(wordStart);
- }
- if (wordEnd == BreakIterator.DONE) {
+ final int textChangeStart = editable.getSpanStart(mRange);
+ final int textChangeEnd = editable.getSpanEnd(mRange);
+
+ Range<Integer> sentenceBoundary = detectSentenceBoundary(editable, textChangeStart,
+ textChangeEnd);
+ int sentenceStart = sentenceBoundary.getLower();
+ int sentenceEnd = sentenceBoundary.getUpper();
+
+ if (sentenceStart == sentenceEnd) {
if (DBG) {
Log.i(TAG, "No more spell check.");
}
@@ -612,29 +678,16 @@ public class SpellChecker implements SpellCheckerSessionListener {
boolean scheduleOtherSpellCheck = false;
- if (wordIteratorWindowEnd < end) {
+ if (sentenceEnd < textChangeEnd) {
if (DBG) {
Log.i(TAG, "schedule other spell check.");
}
// Several batches needed on that region. Cut after last previous word
scheduleOtherSpellCheck = true;
}
- int spellCheckEnd = mWordIterator.preceding(wordIteratorWindowEnd);
- boolean correct = spellCheckEnd != BreakIterator.DONE;
- if (correct) {
- spellCheckEnd = mWordIterator.getEnd(spellCheckEnd);
- correct = spellCheckEnd != BreakIterator.DONE;
- }
- if (!correct) {
- if (DBG) {
- Log.i(TAG, "Incorrect range span.");
- }
- stop();
- return;
- }
+ int spellCheckEnd = sentenceEnd;
do {
- // TODO: Find the start position of the sentence.
- int spellCheckStart = wordStart;
+ int spellCheckStart = sentenceStart;
boolean createSpellCheckSpan = true;
// Cancel or merge overlapped spell check spans
for (int i = 0; i < mLength; ++i) {
@@ -671,27 +724,23 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
// Stop spell checking when there are no characters in the range.
- if (spellCheckEnd < start) {
- break;
- }
if (spellCheckEnd <= spellCheckStart) {
Log.w(TAG, "Trying to spellcheck invalid region, from "
- + start + " to " + end);
+ + sentenceStart + " to " + spellCheckEnd);
break;
}
if (createSpellCheckSpan) {
addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd);
}
} while (false);
- wordStart = spellCheckEnd;
-
- if (scheduleOtherSpellCheck && wordStart != BreakIterator.DONE && wordStart <= end) {
+ sentenceStart = spellCheckEnd;
+ if (scheduleOtherSpellCheck && sentenceStart != BreakIterator.DONE
+ && sentenceStart <= textChangeEnd) {
// Update range span: start new spell check from last wordStart
- setRangeSpan(editable, wordStart, end);
+ setRangeSpan(editable, sentenceStart, textChangeEnd);
} else {
removeRangeSpan(editable);
}
-
spellCheck(mForceCheckWhenEditingWord);
}
@@ -708,6 +757,94 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
}
+ private Range<Integer> detectSentenceBoundary(CharSequence sequence,
+ int textChangeStart, int textChangeEnd) {
+ // Only process a substring of the full text due to performance concern.
+ final int iteratorWindowStart = findSeparator(sequence,
+ Math.max(0, textChangeStart - MAX_SENTENCE_LENGTH),
+ Math.max(0, textChangeStart - 2 * MAX_SENTENCE_LENGTH));
+ final int iteratorWindowEnd = findSeparator(sequence,
+ Math.min(textChangeStart + 2 * MAX_SENTENCE_LENGTH, textChangeEnd),
+ Math.min(textChangeStart + 3 * MAX_SENTENCE_LENGTH, sequence.length()));
+ if (DBG) {
+ Log.d(TAG, "Set iterator window as [" + iteratorWindowStart + ", " + iteratorWindowEnd
+ + ").");
+ }
+ mSentenceIterator.setCharSequence(sequence, iteratorWindowStart, iteratorWindowEnd);
+
+ // Detect the offset of sentence begin/end on the substring.
+ int sentenceStart = mSentenceIterator.isBoundary(textChangeStart) ? textChangeStart
+ : mSentenceIterator.preceding(textChangeStart);
+ int sentenceEnd = mSentenceIterator.following(sentenceStart);
+ if (sentenceEnd == BreakIterator.DONE) {
+ sentenceEnd = iteratorWindowEnd;
+ }
+ if (DBG) {
+ if (sentenceStart != sentenceEnd) {
+ Log.d(TAG, "Sentence detected [" + sentenceStart + ", " + sentenceEnd + ").");
+ }
+ }
+
+ if (sentenceEnd - sentenceStart <= MAX_SENTENCE_LENGTH) {
+ // Add more sentences until the MAX_SENTENCE_LENGTH limitation is reached.
+ while (sentenceEnd < textChangeEnd) {
+ int nextEnd = mSentenceIterator.following(sentenceEnd);
+ if (nextEnd == BreakIterator.DONE
+ || nextEnd - sentenceStart > MAX_SENTENCE_LENGTH) {
+ break;
+ }
+ sentenceEnd = nextEnd;
+ }
+ } else {
+ // If the sentence containing `textChangeStart` is longer than MAX_SENTENCE_LENGTH,
+ // the sentence will be sliced into sub-sentences of about MAX_SENTENCE_LENGTH
+ // characters each. This is done by processing the unchecked part of that sentence :
+ // [textChangeStart, sentenceEnd)
+ //
+ // - If the `uncheckedLength` is bigger than MAX_SENTENCE_LENGTH, then check the
+ // [textChangeStart, textChangeStart + MAX_SENTENCE_LENGTH), and leave the rest
+ // part for the next check.
+ //
+ // - If the `uncheckedLength` is smaller than or equal to MAX_SENTENCE_LENGTH,
+ // then check [sentenceEnd - MAX_SENTENCE_LENGTH, sentenceEnd).
+ //
+ // The offset should be rounded up to word boundary.
+ int uncheckedLength = sentenceEnd - textChangeStart;
+ if (uncheckedLength > MAX_SENTENCE_LENGTH) {
+ sentenceEnd = findSeparator(sequence, sentenceStart + MAX_SENTENCE_LENGTH,
+ sentenceEnd);
+ sentenceStart = roundUpToWordStart(sequence, textChangeStart, sentenceStart);
+ } else {
+ sentenceStart = roundUpToWordStart(sequence, sentenceEnd - MAX_SENTENCE_LENGTH,
+ sentenceStart);
+ }
+ }
+ return new Range(sentenceStart, sentenceEnd);
+ }
+
+ private int roundUpToWordStart(CharSequence sequence, int position, int frontBoundary) {
+ if (isSeparator(sequence.charAt(position))) {
+ return position;
+ }
+ int separator = findSeparator(sequence, position, frontBoundary);
+ return separator != frontBoundary ? separator + 1 : frontBoundary;
+ }
+
+ /**
+ * Search the range [start, end) of sequence and returns the position of the first separator.
+ * If end is smaller than start, do a reverse search.
+ * Returns `end` if no separator is found.
+ */
+ private static int findSeparator(CharSequence sequence, int start, int end) {
+ final int step = start < end ? 1 : -1;
+ for (int i = start; i != end; i += step) {
+ if (isSeparator(sequence.charAt(i))) {
+ return i;
+ }
+ }
+ return end;
+ }
+
public static boolean haveWordBoundariesChanged(final Editable editable, final int start,
final int end, final int spanStart, final int spanEnd) {
final boolean haveWordBoundariesChanged;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fdc66fcb81d8..dba7fa915f35 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -195,6 +195,7 @@ import android.view.textclassifier.TextLinks;
import android.view.textservice.SpellCheckerSubtype;
import android.view.textservice.TextServicesManager;
import android.view.translation.TranslationRequestValue;
+import android.view.translation.UiTranslationController;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
import android.widget.RemoteViews.RemoteView;
@@ -502,7 +503,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private boolean mImeIsConsumingInput;
// Whether cursor is visible without regard to {@link mImeConsumesInput}.
- // {code true} is the default value.
+ // {@code true} is the default value.
private boolean mCursorVisibleFromAttr = true;
static class Drawables {
@@ -9316,7 +9317,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
for (int i = 0; i < n; i++) {
- max = Math.max(max, layout.getLineWidth(i));
+ max = Math.max(max, layout.getLineMax(i));
}
return (int) Math.ceil(max);
@@ -10570,6 +10571,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mEditor == null ? true : mEditor.mCursorVisible;
}
+ /**
+ * @return whether cursor is visible without regard to {@code mImeIsConsumingInput}.
+ * {@code true} is the default value.
+ *
+ * @see #setCursorVisible(boolean)
+ * @hide
+ */
+ public boolean isCursorVisibleFromAttr() {
+ return mCursorVisibleFromAttr;
+ }
+
private boolean canMarquee() {
int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
return width > 0 && (mLayout.getLineWidth(0) > width
@@ -13835,6 +13847,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public ViewTranslationRequest onCreateTranslationRequest() {
if (mText == null || mText.length() == 0) {
+ // TODO(b/182433547): remove before S release
+ if (UiTranslationController.DEBUG) {
+ Log.w(LOG_TAG, "Cannot create translation request for the empty text.");
+ }
return null;
}
// Not translate password, editable text and not important for translation
@@ -13842,6 +13858,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// text selection apis, not support in S.
boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
if (isTextEditable() || isPassword || isTextSelectable()) {
+ // TODO(b/182433547): remove before S release
+ if (UiTranslationController.DEBUG) {
+ Log.w(LOG_TAG, "Cannot create translation request. editable = " + isTextEditable()
+ + ", isPassword = " + isPassword + ", selectable = " + isTextSelectable());
+ }
return null;
}
// TODO(b/176488462): apply the view's important for translation property
@@ -13870,6 +13891,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Restore to original text content.
if (mTranslationTransformation != null) {
setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
+ } else {
+ // TODO(b/182433547): remove before S release
+ Log.w(LOG_TAG, "onPauseUiTranslation(): no translated text.");
}
}
@@ -13889,7 +13913,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mTranslationTransformation != null) {
setTransformationMethod(mTranslationTransformation);
} else {
- Log.w(LOG_TAG, "onResumeTranslatedText(): no translated text.");
+ // TODO(b/182433547): remove before S release
+ Log.w(LOG_TAG, "onRestoreUiTranslation(): no translated text.");
}
}
@@ -13910,6 +13935,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mTranslationTransformation != null) {
setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
mTranslationTransformation = null;
+ } else {
+ // TODO(b/182433547): remove before S release
+ Log.w(LOG_TAG, "onFinishUiTranslation(): no translated text.");
}
}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 8f541d0bd194..3eb35c2c5e8a 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -18,6 +18,7 @@ package android.window;
import android.view.SurfaceControl;
import android.app.ActivityManager;
+import android.graphics.Rect;
import android.window.StartingWindowInfo;
import android.window.WindowContainerToken;
@@ -38,8 +39,12 @@ oneway interface ITaskOrganizer {
/**
* Called when the Task want to remove the starting window.
+ * @param leash A persistent leash for the top window in this task.
+ * @param frame Window frame of the top window.
+ * @param playRevealAnimation Play vanish animation.
*/
- void removeStartingWindow(int taskId);
+ void removeStartingWindow(int taskId, in SurfaceControl leash, in Rect frame,
+ in boolean playRevealAnimation);
/**
* Called when the Task want to copy the splash screen.
diff --git a/core/java/android/window/SizeConfigurationBuckets.aidl b/core/java/android/window/SizeConfigurationBuckets.aidl
new file mode 100644
index 000000000000..adb57f06da7e
--- /dev/null
+++ b/core/java/android/window/SizeConfigurationBuckets.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+parcelable SizeConfigurationBuckets;
diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java
new file mode 100644
index 000000000000..7422f2449a8d
--- /dev/null
+++ b/core/java/android/window/SizeConfigurationBuckets.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.Parcelable;
+import android.util.SparseIntArray;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Arrays;
+
+/**
+ * Contains size-configuration buckets used to prevent excessive configuration changes during
+ * resize.
+ *
+ * These configurations are collected from application's resources based on size-sensitive
+ * qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800
+ * and drawable-sw400dp will be added to both as 400.
+ *
+ * @hide
+ */
+@DataClass(genAidl = true)
+public final class SizeConfigurationBuckets implements Parcelable {
+
+ /** Horizontal (screenWidthDp) buckets */
+ @Nullable
+ private final int[] mHorizontal;
+
+ /** Vertical (screenHeightDp) buckets */
+ @Nullable
+ private final int[] mVertical;
+
+ /** Smallest (smallestScreenWidthDp) buckets */
+ @Nullable
+ private final int[] mSmallest;
+
+ public SizeConfigurationBuckets(Configuration[] sizeConfigurations) {
+ SparseIntArray horizontal = new SparseIntArray();
+ SparseIntArray vertical = new SparseIntArray();
+ SparseIntArray smallest = new SparseIntArray();
+ for (int i = sizeConfigurations.length - 1; i >= 0; i--) {
+ Configuration config = sizeConfigurations[i];
+ if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ vertical.put(config.screenHeightDp, 0);
+ }
+ if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ horizontal.put(config.screenWidthDp, 0);
+ }
+ if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ smallest.put(config.smallestScreenWidthDp, 0);
+ }
+ }
+ mHorizontal = horizontal.copyKeys();
+ mVertical = vertical.copyKeys();
+ mSmallest = smallest.copyKeys();
+ }
+
+ /**
+ * Get the changes between two configurations but don't count changes in sizes if they don't
+ * cross boundaries that are important to the app.
+ *
+ * This is a static helper to deal with null `buckets`. When no buckets have been specified,
+ * this actually filters out all 3 size-configs. This is legacy behavior.
+ */
+ public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig,
+ @Nullable SizeConfigurationBuckets buckets) {
+ if (buckets == null) {
+ return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE);
+ }
+ if ((diff & CONFIG_SCREEN_SIZE) != 0) {
+ final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp,
+ newConfig.screenWidthDp)
+ || buckets.crossesVerticalSizeThreshold(oldConfig.screenHeightDp,
+ newConfig.screenHeightDp);
+ if (!crosses) {
+ diff &= ~CONFIG_SCREEN_SIZE;
+ }
+ }
+ if ((diff & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+ final int oldSmallest = oldConfig.smallestScreenWidthDp;
+ final int newSmallest = newConfig.smallestScreenWidthDp;
+ if (!buckets.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
+ diff &= ~CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+ }
+ return diff;
+ }
+
+ private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
+ return crossesSizeThreshold(mHorizontal, firstDp, secondDp);
+ }
+
+ private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) {
+ return crossesSizeThreshold(mVertical, firstDp, secondDp);
+ }
+
+ private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) {
+ return crossesSizeThreshold(mSmallest, firstDp, secondDp);
+ }
+
+ /**
+ * The purpose of this method is to decide whether the activity needs to be relaunched upon
+ * changing its size. In most cases the activities don't need to be relaunched, if the resize
+ * is small, all the activity content has to do is relayout itself within new bounds. There are
+ * cases however, where the activity's content would be completely changed in the new size and
+ * the full relaunch is required.
+ *
+ * The activity will report to us vertical and horizontal thresholds after which a relaunch is
+ * required. These thresholds are collected from the application resource qualifiers. For
+ * example, if application has layout-w600dp resource directory, then it needs a relaunch when
+ * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if
+ * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side
+ * of the threshold.
+ */
+ private static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
+ int secondDp) {
+ if (thresholds == null) {
+ return false;
+ }
+ for (int i = thresholds.length - 1; i >= 0; i--) {
+ final int threshold = thresholds[i];
+ if ((firstDp < threshold && secondDp >= threshold)
+ || (firstDp >= threshold && secondDp < threshold)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " "
+ + Arrays.toString(mSmallest);
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/SizeConfigurationBuckets.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new SizeConfigurationBuckets.
+ *
+ * @param horizontal
+ * Horizontal (screenWidthDp) buckets
+ * @param vertical
+ * Vertical (screenHeightDp) buckets
+ * @param smallest
+ * Smallest (smallestScreenWidthDp) buckets
+ */
+ @DataClass.Generated.Member
+ public SizeConfigurationBuckets(
+ @Nullable int[] horizontal,
+ @Nullable int[] vertical,
+ @Nullable int[] smallest) {
+ this.mHorizontal = horizontal;
+ this.mVertical = vertical;
+ this.mSmallest = smallest;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * Horizontal (screenWidthDp) buckets
+ */
+ @DataClass.Generated.Member
+ public @Nullable int[] getHorizontal() {
+ return mHorizontal;
+ }
+
+ /**
+ * Vertical (screenHeightDp) buckets
+ */
+ @DataClass.Generated.Member
+ public @Nullable int[] getVertical() {
+ return mVertical;
+ }
+
+ /**
+ * Smallest (smallestScreenWidthDp) buckets
+ */
+ @DataClass.Generated.Member
+ public @Nullable int[] getSmallest() {
+ return mSmallest;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mHorizontal != null) flg |= 0x1;
+ if (mVertical != null) flg |= 0x2;
+ if (mSmallest != null) flg |= 0x4;
+ dest.writeByte(flg);
+ if (mHorizontal != null) dest.writeIntArray(mHorizontal);
+ if (mVertical != null) dest.writeIntArray(mVertical);
+ if (mSmallest != null) dest.writeIntArray(mSmallest);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ SizeConfigurationBuckets(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray();
+ int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray();
+ int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray();
+
+ this.mHorizontal = horizontal;
+ this.mVertical = vertical;
+ this.mSmallest = smallest;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<SizeConfigurationBuckets> CREATOR
+ = new Parcelable.Creator<SizeConfigurationBuckets>() {
+ @Override
+ public SizeConfigurationBuckets[] newArray(int size) {
+ return new SizeConfigurationBuckets[size];
+ }
+
+ @Override
+ public SizeConfigurationBuckets createFromParcel(@NonNull android.os.Parcel in) {
+ return new SizeConfigurationBuckets(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1615845864280L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java",
+ inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\nprivate static boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 35ccfca101d3..ddea64a77f4b 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -23,6 +23,7 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -77,7 +78,8 @@ public final class SplashScreenView extends FrameLayout {
private Animatable mAnimatableIcon;
private ValueAnimator mAnimator;
-
+ // The host activity when transfer view to it.
+ private Activity mHostActivity;
// cache original window and status
private Window mWindow;
private boolean mDrawBarBackground;
@@ -85,7 +87,7 @@ public final class SplashScreenView extends FrameLayout {
private int mNavigationBarColor;
/**
- * Internal builder to create a SplashScreenWindowView object.
+ * Internal builder to create a SplashScreenView object.
* @hide
*/
public static class Builder {
@@ -331,6 +333,17 @@ public final class SplashScreenView extends FrameLayout {
restoreSystemUIColors();
mWindow = null;
}
+ if (mHostActivity != null) {
+ mHostActivity.detachSplashScreenView();
+ }
+ }
+
+ /**
+ * Called when this view is attached to an activity.
+ * @hide
+ */
+ public void attachHostActivity(Activity activity) {
+ mHostActivity = activity;
}
/**
@@ -391,7 +404,7 @@ public final class SplashScreenView extends FrameLayout {
* Get the initial background color of this view.
* @hide
*/
- @ColorInt int getInitBackgroundColor() {
+ public @ColorInt int getInitBackgroundColor() {
return mInitBackgroundColor;
}
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index d1c1e40d1578..c7672dc9fe7d 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -126,6 +126,12 @@ public final class StartingWindowInfo implements Parcelable {
*/
public int splashScreenThemeResId;
+ /**
+ * Is keyguard occluded on default display.
+ * @hide
+ */
+ public boolean isKeyguardOccluded = false;
+
public StartingWindowInfo() {
}
@@ -147,6 +153,7 @@ public final class StartingWindowInfo implements Parcelable {
dest.writeTypedObject(topOpaqueWindowLayoutParams, flags);
dest.writeTypedObject(mainWindowLayoutParams, flags);
dest.writeInt(splashScreenThemeResId);
+ dest.writeBoolean(isKeyguardOccluded);
}
void readFromParcel(@NonNull Parcel source) {
@@ -157,6 +164,7 @@ public final class StartingWindowInfo implements Parcelable {
WindowManager.LayoutParams.CREATOR);
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
splashScreenThemeResId = source.readInt();
+ isKeyguardOccluded = source.readBoolean();
}
@Override
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 04020ec3ee8a..3340cf4fb707 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
@@ -100,9 +101,14 @@ public class TaskOrganizer extends WindowOrganizer {
/**
* Called when the Task want to remove the starting window.
+ * @param leash A persistent leash for the top window in this task. Release it once exit
+ * animation has finished.
+ * @param frame Window frame of the top window.
+ * @param playRevealAnimation Play vanish animation.
*/
@BinderThread
- public void removeStartingWindow(int taskId) {}
+ public void removeStartingWindow(int taskId, @Nullable SurfaceControl leash,
+ @Nullable Rect frame, boolean playRevealAnimation) {}
/**
* Called when the Task want to copy the splash screen.
@@ -217,15 +223,16 @@ public class TaskOrganizer extends WindowOrganizer {
private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
@Override
-
public void addStartingWindow(StartingWindowInfo windowInfo,
IBinder appToken) {
mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken));
}
@Override
- public void removeStartingWindow(int taskId) {
- mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId));
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
+ mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId, leash, frame,
+ playRevealAnimation));
}
@Override
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index c2e1426bc4fe..7baa53bcd56d 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -16,6 +16,9 @@
package com.android.internal.accessibility.util;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -28,6 +31,10 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
import android.content.ComponentName;
import android.view.accessibility.AccessibilityManager;
@@ -113,6 +120,19 @@ public final class AccessibilityStatsLogUtils {
UNKNOWN_STATUS);
}
+ /**
+ * Logs the magnification activated mode and its duration of the usage.
+ * Calls this when the magnification is disabled.
+ *
+ * @param mode The activated magnification mode.
+ * @param duration The duration in milliseconds during the magnification is activated.
+ */
+ public static void logMagnificationUsageState(int mode, long duration) {
+ FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED,
+ convertToLoggingMagnificationMode(mode),
+ duration);
+ }
+
private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON:
@@ -127,4 +147,18 @@ public final class AccessibilityStatsLogUtils {
return enabled ? ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED
: ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED;
}
+
+ private static int convertToLoggingMagnificationMode(int mode) {
+ switch (mode) {
+ case ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
+ return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
+ case ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
+ return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
+ case ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
+ return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
+
+ default:
+ return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 6cfd49888fac..d4d853624700 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1119,7 +1119,7 @@ public class ChooserActivity extends ResolverActivity implements
ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
Context.CLIPBOARD_SERVICE);
- clipboardManager.setPrimaryClip(clipData);
+ clipboardManager.setPrimaryClipAsPackage(clipData, getReferrerPackageName());
Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
// Log share completion via copy
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index c1952c7d52cf..957e416986e0 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -166,4 +166,15 @@ interface IBatteryStats {
/** {@hide} */
boolean setChargingStateUpdateDelayMillis(int delay);
+
+ /** Exposed as a test API. */
+ void setChargerAcOnline(boolean online, boolean forceUpdate);
+ /** Exposed as a test API. */
+ void setBatteryLevel(int level, boolean forceUpdate);
+ /** Exposed as a test API. */
+ void unplugBattery(boolean forceUpdate);
+ /** Exposed as a test API. */
+ void resetBattery(boolean forceUpdate);
+ /** Exposed as a test API. */
+ void suspendBatteryInput();
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ccb980eb82b7..592f7c7e1925 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.media.permission.Identity;
import android.os.Bundle;
import android.os.RemoteCallback;
+import android.os.SharedMemory;
import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -225,14 +226,19 @@ interface IVoiceInteractionManagerService {
IBinder client);
/**
- * Sets hotword detection configuration.
+ * Set configuration and pass read-only data to hotword detection service.
*
- * Note: Currently it will trigger hotword detection service after calling this function when
- * all conditions meet the requirements.
- *
- * @param options Config data.
- * @return {@link VoiceInteractionService#HOTWORD_CONFIG_SUCCESS} in case of success,
- * {@link VoiceInteractionService#HOTWORD_CONFIG_FAILURE} in case of failure.
+ * @param options Application configuration data provided by the
+ * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+ * contents that can be used to communicate with other processes.
+ * @param sharedMemory The unrestricted data blob provided by the
+ * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * such data to the trusted process.
+ */
+ void setHotwordDetectionServiceConfig(in Bundle options, in SharedMemory sharedMemory);
+
+ /**
+ * Requests to shutdown hotword detection service.
*/
- int setHotwordDetectionConfig(in Bundle options);
+ void shutdownHotwordDetectionService();
}
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 0b937fad7df1..364db06976a0 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -31,4 +31,14 @@ public class AndroidBuildClassifier {
public boolean isFinalBuild() {
return "REL".equals(Build.VERSION.CODENAME);
}
+
+ /**
+ * The current platform SDK version.
+ */
+ public int platformTargetSdk() {
+ if (isFinalBuild()) {
+ return Build.VERSION.SDK_INT;
+ }
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
}
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index c0bbe5082131..e408be2ab471 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -34,7 +34,8 @@ public final class OverrideAllowedState implements Parcelable {
DISABLED_NON_TARGET_SDK,
DISABLED_TARGET_SDK_TOO_HIGH,
DEFERRED_VERIFICATION,
- LOGGING_ONLY_CHANGE
+ LOGGING_ONLY_CHANGE,
+ PLATFORM_TOO_OLD
})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
@@ -65,6 +66,10 @@ public final class OverrideAllowedState implements Parcelable {
* Change is marked as logging only, and cannot be toggled.
*/
public static final int LOGGING_ONLY_CHANGE = 5;
+ /**
+ * Change is gated by a target sdk version newer than the current platform sdk version.
+ */
+ public static final int PLATFORM_TOO_OLD = 6;
@State
public final int state;
@@ -123,6 +128,11 @@ public final class OverrideAllowedState implements Parcelable {
throw new SecurityException(String.format(
"Cannot override %1$d because it is marked as a logging-only change.",
changeId));
+ case PLATFORM_TOO_OLD:
+ throw new SecurityException(String.format(
+ "Cannot override %1$d for %2$s because the change's targetSdk threshold "
+ + "(%3$d) is above the platform sdk.",
+ changeId, packageName, changeIdTargetSdk));
}
}
@@ -170,6 +180,8 @@ public final class OverrideAllowedState implements Parcelable {
return "DEFERRED_VERIFICATION";
case LOGGING_ONLY_CHANGE:
return "LOGGING_ONLY_CHANGE";
+ case PLATFORM_TOO_OLD:
+ return "PLATFORM_TOO_OLD";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ee988781e51d..52801faf9c36 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -474,6 +474,12 @@ public final class SystemUiDeviceConfigFlags {
public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets";
+ /*
+ * (long) The duration that the home button must be pressed before triggering Assist
+ */
+ public static final String HOME_BUTTON_LONG_PRESS_DURATION_MS =
+ "home_button_long_press_duration_ms";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index e602cd2c8890..a60b31078a86 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -504,8 +504,10 @@ public abstract class FileSystemProvider extends DocumentsProvider {
final File visibleFile = getFileForDocId(documentId, true);
final int pfdMode = ParcelFileDescriptor.parseMode(mode);
- if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) {
- return openFileForRead(file);
+ if (visibleFile == null) {
+ return ParcelFileDescriptor.open(file, pfdMode);
+ } else if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) {
+ return openFileForRead(visibleFile);
} else {
try {
// When finished writing, kick off media scanner
@@ -522,6 +524,10 @@ public abstract class FileSystemProvider extends DocumentsProvider {
private ParcelFileDescriptor openFileForRead(final File target) throws FileNotFoundException {
final Uri uri = MediaStore.scanFile(getContext().getContentResolver(), target);
+ if (uri == null) {
+ Log.w(TAG, "Failed to retrieve media store URI for: " + target);
+ return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
// Passing the calling uid via EXTRA_MEDIA_CAPABILITIES_UID, so that the decision to
// transcode or not transcode can be made based upon the calling app's uid, and not based
@@ -532,7 +538,8 @@ public abstract class FileSystemProvider extends DocumentsProvider {
final AssetFileDescriptor afd =
getContext().getContentResolver().openTypedAssetFileDescriptor(uri, "*/*", opts);
if (afd == null) {
- return null;
+ Log.w(TAG, "Failed to open with media_capabilities uid for URI: " + uri);
+ return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY);
}
return afd.getParcelFileDescriptor();
diff --git a/core/java/com/android/internal/graphics/palette/Mean.java b/core/java/com/android/internal/graphics/palette/Mean.java
index 894f91b6261c..bde036349d3b 100644
--- a/core/java/com/android/internal/graphics/palette/Mean.java
+++ b/core/java/com/android/internal/graphics/palette/Mean.java
@@ -22,20 +22,19 @@ import java.util.Random;
* Represents a centroid in Kmeans algorithms.
*/
public class Mean {
- private static final Random RANDOM = new Random(0);
-
public float[] center;
/**
* Constructor.
*
* @param upperBound maximum value of a dimension in the space Kmeans is optimizing in
+ * @param random used to generate a random center
*/
- Mean(int upperBound) {
+ Mean(int upperBound, Random random) {
center =
new float[]{
- RANDOM.nextInt(upperBound + 1), RANDOM.nextInt(upperBound + 1),
- RANDOM.nextInt(upperBound + 1)
+ random.nextInt(upperBound + 1), random.nextInt(upperBound + 1),
+ random.nextInt(upperBound + 1)
};
}
diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
index a87a34f4ae11..1d865c2513cf 100644
--- a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
@@ -57,13 +58,27 @@ public class WSMeansQuantizer implements Quantizer {
}
if (maxColors > means.length) {
+ // Always initialize Random with the same seed. Ensures the results of quantization
+ // are consistent, even when random centroids are required.
+ Random random = new Random(0x42688);
int randomMeansToCreate = maxColors - means.length;
for (int i = 0; i < randomMeansToCreate; i++) {
- mMeans[means.length + i] = new Mean(100);
+ mMeans[means.length + i] = new Mean(100, random);
}
}
for (int pixel : pixels) {
+ // These are pixels from the bitmap that is being quantized.
+ // Depending on the bitmap & downscaling, it may have pixels that are less than opaque
+ // Ignore those pixels.
+ ///
+ // Note: they don't _have_ to be ignored, for example, we could instead turn them
+ // opaque. Traditionally, including outside Android, quantizers ignore transparent
+ // pixels, so that strategy was chosen.
+ int alpha = (pixel >> 24) & 0xff;
+ if (alpha < 255) {
+ continue;
+ }
Integer currentCount = mCountByColor.get(pixel);
if (currentCount == null) {
currentCount = 0;
@@ -105,8 +120,12 @@ public class WSMeansQuantizer implements Quantizer {
/** Create random starting centroids for K-means. */
public static float[][] randomMeans(int maxColors, int upperBound) {
float[][] means = new float[maxColors][];
+
+ // Always initialize Random with the same seed. Ensures the results of quantization
+ // are consistent, even when random centroids are required.
+ Random random = new Random(0x42688);
for (int i = 0; i < maxColors; i++) {
- means[i] = new Mean(upperBound).center;
+ means[i] = new Mean(upperBound, random).center;
}
return means;
}
diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
index 66206bf8297a..a2652ea6d5e1 100644
--- a/core/java/com/android/internal/graphics/palette/WuQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
@@ -78,7 +78,12 @@ public class WuQuantizer implements Quantizer {
// All of the sample Wu implementations are reimplementations of a snippet of C code from
// the early 90s. They all cap the maximum # of colors at 256, and it is impossible to tell
// if this is a requirement, a consequence of QUANT_SIZE, or arbitrary.
- this.mMaxColors = Math.min(MAX_COLORS, maxColorCount);
+ //
+ // Also, the number of maximum colors should be capped at the number of pixels - otherwise,
+ // If extraction is run on a set of pixels whose count is less than max colors,
+ // then colors.length < max colors, and accesses to colors[index] throw an
+ // ArrayOutOfBoundsException.
+ this.mMaxColors = Math.min(Math.min(MAX_COLORS, maxColorCount), colors.length);
Box[] cube = new Box[mMaxColors];
int red, green, blue;
@@ -119,17 +124,13 @@ public class WuQuantizer implements Quantizer {
}
}
- // If extraction is run on a set of pixels whose count is less than the
- // number of max colors, then colors.length < max colors, and accesses
- // to colors[index] inside the for loop throw an ArrayOutOfBoundsException.
- int numColorsToCreate = (int) Math.min(mMaxColors, colors.length);
- for (k = 0; k < numColorsToCreate; ++k) {
+ for (k = 0; k < mMaxColors; ++k) {
weight = getVolume(cube[k], mWt);
if (weight > 0) {
red = (int) (getVolume(cube[k], mMr) / weight);
green = (int) (getVolume(cube[k], mMg) / weight);
blue = (int) (getVolume(cube[k], mMb) / weight);
- colors[k] = ((red & 0x0ff) << 16) | ((green & 0x0ff) << 8) | (blue & 0x0ff);
+ colors[k] = (255 << 24) | (red << 16) | (green << 8) | blue;
} else {
colors[k] = 0;
}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 5829047ad642..7f0178e29d85 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -206,9 +206,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Cancel the trace session of the CUJ.
*/
public synchronized void cancel() {
- // The session is ongoing, end the trace session.
- // That means the cancel call is from external invocation, not from end().
- if (mBeginVsyncId != INVALID_ID && mEndVsyncId == INVALID_ID) {
+ // We don't need to end the trace section if it never begun.
+ if (mBeginVsyncId != INVALID_ID) {
Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
}
mCancelled = true;
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 2237efc9e2b6..2f40d3b457c6 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -58,6 +58,7 @@ public class SystemNotificationChannels {
public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
public static String DO_NOT_DISTURB = "DO_NOT_DISTURB";
public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION";
+ public static String ACCESSIBILITY_SECURITY_POLICY = "ACCESSIBILITY_SECURITY_POLICY";
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -199,6 +200,12 @@ public class SystemNotificationChannels {
newFeaturePrompt.setBlockable(true);
channelsList.add(newFeaturePrompt);
+ final NotificationChannel accessibilitySecurityPolicyChannel = new NotificationChannel(
+ ACCESSIBILITY_SECURITY_POLICY,
+ context.getString(R.string.notification_channel_accessibility_security_policy),
+ NotificationManager.IMPORTANCE_LOW);
+ channelsList.add(accessibilitySecurityPolicyChannel);
+
nm.createNotificationChannels(channelsList);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9ecb0ad09bc4..33b55ac2f0a0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -169,7 +169,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 194;
+ static final int VERSION = 195;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1009,8 +1009,12 @@ public class BatteryStatsImpl extends BatteryStats {
protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats;
/** Last known screen state. Needed for apportioning display energy. */
int mScreenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
+ /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */
+ @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
/** Cpu Power calculator for attributing measured cpu charge consumption to uids */
@Nullable CpuPowerCalculator mCpuPowerCalculator = null;
+ /** Wifi Power calculator for attributing measured wifi charge consumption to uids */
+ @Nullable WifiPowerCalculator mWifiPowerCalculator = null;
/**
* These provide time bases that discount the time the device is plugged
@@ -6967,6 +6971,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public long getBluetoothMeasuredBatteryConsumptionUC() {
+ return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
+ }
+
+ @Override
+ public long getCpuMeasuredBatteryConsumptionUC() {
+ return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+ }
+
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@@ -6976,6 +6990,11 @@ public class BatteryStatsImpl extends BatteryStats {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
}
+ @Override
+ public long getWifiMeasuredBatteryConsumptionUC() {
+ return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
+ }
+
/**
* Returns the consumption (in microcoulombs) that the given standard power bucket consumed.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable
@@ -7810,6 +7829,26 @@ public class BatteryStatsImpl extends BatteryStats {
return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
}
+ @Override
+ public long getBluetoothMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
+ }
+
+ @Override
+ public long getCpuMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+ }
+
+ @Override
+ public long getScreenOnMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
+ }
+
+ @Override
+ public long getWifiMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
+ }
+
/**
* Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
* since last marked. Also sets the mark time for both these timers.
@@ -8477,11 +8516,6 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- @Override
- public long getScreenOnMeasuredBatteryConsumptionUC() {
- return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
- }
-
void initNetworkActivityLocked() {
detachIfNotNull(mNetworkByteActivityCounters);
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -11427,7 +11461,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @param info The energy information from the WiFi controller.
*/
public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
- long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
}
@@ -11449,9 +11483,21 @@ public class BatteryStatsImpl extends BatteryStats {
if (delta != null) {
mNetworkStatsPool.release(delta);
}
+ if (mIgnoreNextExternalStats) {
+ // TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the
+ // global one) here like we do for display. But I'm not sure it's worth the
+ // complicated code for a codepath that shouldn't ever actually happen in real
+ // life.
+ }
return;
}
+ final ArrayMap<Uid, Double> uidEstimatedConsumptionMah =
+ (mGlobalMeasuredEnergyStats != null
+ && mWifiPowerCalculator != null && consumedChargeUC > 0) ?
+ new ArrayMap<>() : null;
+ double totalEstimatedConsumptionMah = 0;
+
SparseLongArray rxPackets = new SparseLongArray();
SparseLongArray txPackets = new SparseLongArray();
long totalTxPackets = 0;
@@ -11486,6 +11532,7 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
entry.rxPackets);
+ // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
rxPackets.put(u.getUid(), entry.rxPackets);
// Sum the total number of packets so that the Rx Power can
@@ -11505,12 +11552,42 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
entry.txPackets);
+ // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
txPackets.put(u.getUid(), entry.txPackets);
// Sum the total number of packets so that the Tx Power can
// be evenly distributed amongst the apps.
totalTxPackets += entry.txPackets;
}
+
+ // Calculate consumed energy for this uid. Only do so if WifiReporting isn't
+ // enabled (if it is, we'll do it later instead using info).
+ if (uidEstimatedConsumptionMah != null && info == null && !mHasWifiReporting) {
+ final long uidRunningMs = u.mWifiRunningTimer
+ .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+ if (uidRunningMs > 0) u.mWifiRunningTimer.setMark(elapsedRealtimeMs);
+
+ final long uidScanMs = u.mWifiScanTimer
+ .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+ if (uidScanMs > 0) u.mWifiScanTimer.setMark(elapsedRealtimeMs);
+
+ long uidBatchScanMs = 0;
+ for (int bn = 0; bn < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bn++) {
+ if (u.mWifiBatchedScanTimer[bn] != null) {
+ long bnMs = u.mWifiBatchedScanTimer[bn]
+ .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+ if (bnMs > 0) {
+ u.mWifiBatchedScanTimer[bn].setMark(elapsedRealtimeMs);
+ }
+ uidBatchScanMs += bnMs;
+ }
+ }
+
+ addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+ mWifiPowerCalculator.calcPowerWithoutControllerDataMah(
+ entry.rxPackets, entry.txPackets,
+ uidRunningMs, uidScanMs, uidBatchScanMs));
+ }
}
mNetworkStatsPool.release(delta);
delta = null;
@@ -11571,15 +11648,14 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
- long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
+ final long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
+ long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final
+ long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final
if (scanTimeSinceMarkMs > 0) {
// Set the new mark so that next time we get new data since this point.
uid.mWifiScanTimer.setMark(elapsedRealtimeMs);
- long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs;
- long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs;
-
// Our total scan time is more than the reported Tx/Rx time.
// This is possible because the cost of a scan is approximate.
// Let's normalize the result so that we evenly blame each app
@@ -11613,6 +11689,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Distribute evenly the power consumed while Idle to each app holding a WiFi
// lock.
+ long myIdleTimeMs = 0;
final long wifiLockTimeSinceMarkMs =
uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000) / 1000;
@@ -11620,8 +11697,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Set the new mark so that next time we get new data since this point.
uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs);
- final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs)
- / totalWifiLockTimeMs;
+ myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) / totalWifiLockTimeMs;
if (DEBUG_ENERGY) {
Slog.d(TAG, " IdleTime for UID " + uid.getUid() + ": "
+ myIdleTimeMs + " ms");
@@ -11629,6 +11705,12 @@ public class BatteryStatsImpl extends BatteryStats {
uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter()
.addCountLocked(myIdleTimeMs);
}
+
+ if (uidEstimatedConsumptionMah != null) {
+ double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah(
+ scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs);
+ addDoubleToUidMap(uidEstimatedConsumptionMah, uid, uidEstMah);
+ }
}
if (DEBUG_ENERGY) {
@@ -11648,6 +11730,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0]
.addCountLocked(myTxTimeMs);
+ if (uidEstimatedConsumptionMah != null) {
+ addDoubleToUidMap(uidEstimatedConsumptionMah, uid,
+ mWifiPowerCalculator.calcPowerFromControllerDataMah(
+ 0, myTxTimeMs, 0));
+ }
}
// Distribute the remaining Rx power appropriately between all apps that received
@@ -11662,6 +11749,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter()
.addCountLocked(myRxTimeMs);
+ if (uidEstimatedConsumptionMah != null) {
+ addDoubleToUidMap(uidEstimatedConsumptionMah, uid,
+ mWifiPowerCalculator.calcPowerFromControllerDataMah(
+ myRxTimeMs, 0, 0));
+ }
}
// Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
@@ -11680,10 +11772,11 @@ public class BatteryStatsImpl extends BatteryStats {
// POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+ double controllerMaMs = 0;
if (opVolt != 0) {
// We store the power drain as mAms.
- mWifiActivity.getPowerCounter().addCountLocked(
- (long) (info.getControllerEnergyUsedMicroJoules() / opVolt));
+ controllerMaMs = info.getControllerEnergyUsedMicroJoules() / opVolt;
+ mWifiActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
}
// Converting uWs to mAms.
// Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
@@ -11695,6 +11788,29 @@ public class BatteryStatsImpl extends BatteryStats {
(monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mTmpRailStats.resetWifiTotalEnergyUsed();
+
+ if (uidEstimatedConsumptionMah != null) {
+ totalEstimatedConsumptionMah = Math.max(controllerMaMs / MILLISECONDS_IN_HOUR,
+ mWifiPowerCalculator.calcPowerFromControllerDataMah(
+ rxTimeMs, txTimeMs, idleTimeMs));
+ }
+ }
+
+ // Update the MeasuredEnergyStats information.
+ if (uidEstimatedConsumptionMah != null) {
+ mGlobalMeasuredEnergyStats.updateStandardBucket(
+ MeasuredEnergyStats.POWER_BUCKET_WIFI, consumedChargeUC);
+
+ // Now calculate the consumption for each uid, according to its proportional usage.
+ if (!mHasWifiReporting) {
+ final long globalTimeMs = mGlobalWifiRunningTimer
+ .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+ mGlobalWifiRunningTimer.setMark(elapsedRealtimeMs);
+ totalEstimatedConsumptionMah = mWifiPowerCalculator
+ .calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
+ }
+ distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah);
}
}
}
@@ -11938,7 +12054,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @param info The energy information from the bluetooth controller.
*/
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
- long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating bluetooth stats: " + info);
}
@@ -11970,6 +12086,11 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms");
}
+ final ArrayMap<Uid, Double> uidEstimatedConsumptionMah =
+ (mGlobalMeasuredEnergyStats != null
+ && mBluetoothPowerCalculator != null && consumedChargeUC > 0) ?
+ new ArrayMap<>() : null;
+
long totalScanTimeMs = 0;
final int uidCount = mUidStats.size();
@@ -12028,6 +12149,12 @@ public class BatteryStatsImpl extends BatteryStats {
counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs);
counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs);
+ if (uidEstimatedConsumptionMah != null) {
+ addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+ mBluetoothPowerCalculator.calculatePowerMah(
+ scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0));
+ }
+
leftOverRxTimeMs -= scanTimeRxSinceMarkMs;
leftOverTxTimeMs -= scanTimeTxSinceMarkMs;
}
@@ -12088,6 +12215,11 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
}
counter.getRxTimeCounter().addCountLocked(timeRxMs);
+
+ if (uidEstimatedConsumptionMah != null) {
+ addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+ mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0));
+ }
}
if (totalTxBytes > 0 && txBytes > 0) {
@@ -12096,6 +12228,11 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
}
counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
+
+ if (uidEstimatedConsumptionMah != null) {
+ addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+ mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0));
+ }
}
}
}
@@ -12107,12 +12244,26 @@ public class BatteryStatsImpl extends BatteryStats {
// POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+ double controllerMaMs = 0;
if (opVolt != 0) {
+ controllerMaMs = (info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
+ / opVolt;
// We store the power drain as mAms.
- mBluetoothActivity.getPowerCounter().addCountLocked(
- (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
- / opVolt));
+ mBluetoothActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
}
+
+ // Update the MeasuredEnergyStats information.
+ if (uidEstimatedConsumptionMah != null) {
+ mGlobalMeasuredEnergyStats.updateStandardBucket(
+ MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC);
+
+ double totalEstimatedMah
+ = mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
+ totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
+ distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah);
+ }
+
mLastBluetoothActivityInfo.set(info);
}
@@ -12301,37 +12452,17 @@ public class BatteryStatsImpl extends BatteryStats {
// If multidisplay becomes a reality, this is probably more reasonable than pooling.
// On the first pass, collect total time since mark so that we can normalize power.
- long totalFgTimeMs = 0L;
- final ArrayMap<Uid, Long> fgTimeMsArray = new ArrayMap<>();
+ final ArrayMap<Uid, Double> fgTimeUsArray = new ArrayMap<>();
final long elapsedRealtimeUs = elapsedRealtimeMs * 1000;
// TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
final int uidStatsSize = mUidStats.size();
for (int i = 0; i < uidStatsSize; i++) {
final Uid uid = mUidStats.valueAt(i);
- final long fgTimeMs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true) / 1000;
- if (fgTimeMs == 0) continue;
- fgTimeMsArray.put(uid, fgTimeMs);
- totalFgTimeMs += fgTimeMs;
- }
- long totalDisplayChargeMC = chargeUC / 1000; // not final
-
- // Actually assign and distribute power usage to apps based on their fg time since mark.
- // TODO(b/175726326): Decide on 'energy' units and make sure algorithm won't overflow.
- final long fgTimeArraySize = fgTimeMsArray.size();
- for (int i = 0; i < fgTimeArraySize; i++) {
- final Uid uid = fgTimeMsArray.keyAt(i);
- final long fgTimeMs = fgTimeMsArray.valueAt(i);
-
- // Using long division: "appEnergy = totalEnergy * appFg/totalFg + 0.5" with rounding
- final long appDisplayChargeMC =
- (totalDisplayChargeMC * fgTimeMs + (totalFgTimeMs / 2))
- / totalFgTimeMs;
- uid.addChargeToStandardBucketLocked(appDisplayChargeMC * 1000, powerBucket);
-
- // To mitigate round-off errors, remove this app from numerator & denominator totals
- totalDisplayChargeMC -= appDisplayChargeMC;
- totalFgTimeMs -= fgTimeMs;
+ final long fgTimeUs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true);
+ if (fgTimeUs == 0) continue;
+ fgTimeUsArray.put(uid, (double) fgTimeUs);
}
+ distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0);
}
/**
@@ -12379,6 +12510,54 @@ public class BatteryStatsImpl extends BatteryStats {
}
/**
+ * Attributes energy (for the given bucket) to each uid according to the following formula:
+ * blamedEnergy[uid] = totalEnergy * ratioNumerators[uid] / ratioDenominator;
+ * <p>Does nothing if ratioDenominator is 0.
+ *
+ * <p>Here, ratioDenominator = max(sumOfAllRatioNumerators, minRatioDenominator),
+ * so if given minRatioDenominator <= 0, then sumOfAllRatioNumerators will be used implicitly.
+ *
+ * <p>Note that ratioNumerators and minRatioDenominator must use the same units, but need not
+ * use the same units as totalConsumedChargeUC (which must be in microcoulombs).
+ *
+ * <p>A consequence of minRatioDenominator is that the sum over all uids might be less than
+ * totalConsumedChargeUC. This is intentional; the remainder is purposefully unnaccounted rather
+ * than incorrectly blamed on uids, and implies unknown (non-uid) sources of drain.
+ */
+ // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>.
+ private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
+ long totalConsumedChargeUC, ArrayMap<Uid, Double> ratioNumerators,
+ double minRatioDenominator) {
+
+ // If the sum of all app usage was greater than the total, use that instead:
+ double sumRatioNumerators = 0;
+ for (int i = ratioNumerators.size() - 1; i >= 0; i--) {
+ sumRatioNumerators += ratioNumerators.valueAt(i);
+ }
+ final double ratioDenominator = Math.max(sumRatioNumerators, minRatioDenominator);
+ if (ratioDenominator <= 0) return;
+
+ for (int i = ratioNumerators.size() - 1; i >= 0; i--) {
+ final Uid uid = ratioNumerators.keyAt(i);
+ final double ratioNumerator = ratioNumerators.valueAt(i);
+ final long uidActualUC
+ = (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5);
+ uid.addChargeToStandardBucketLocked(uidActualUC, bucket);
+ }
+ }
+
+ /** Adds the summand to the value stored in uidMap for the given uid. */
+ // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>.
+ private static void addDoubleToUidMap(ArrayMap<Uid, Double> uidMap, Uid uid, double summand) {
+ if (uidMap == null) return;
+ final Double oldVal = uidMap.get(uid);
+ if (oldVal != null) {
+ summand += oldVal;
+ }
+ uidMap.put(uid, summand);
+ }
+
+ /**
* Read and record Rail Energy data.
*/
public void updateRailStatsLocked() {
@@ -14212,15 +14391,20 @@ public class BatteryStatsImpl extends BatteryStats {
if (mGlobalMeasuredEnergyStats == null) {
mGlobalMeasuredEnergyStats =
new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- return;
} else {
supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
supportedStandardBuckets, numCustomBuckets);
}
+ if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) {
+ mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
+ }
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) {
mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
}
+ if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
+ mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
+ }
}
if (supportedBucketMismatch) {
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 7d42de4486a4..db1403479f8a 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -15,8 +15,11 @@
*/
package com.android.internal.os;
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryStats.ControllerActivityCounter;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
@@ -65,17 +68,19 @@ public class BluetoothPowerCalculator extends PowerCalculator {
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
- calculateApp(app, total);
+ calculateApp(app, total, query);
if (app.getUid() == Process.BLUETOOTH_UID) {
app.excludeFromBatteryUsageStats();
systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
}
}
- final BatteryStats.ControllerActivityCounter activityCounter =
+ final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ?
+ POWER_DATA_UNAVAILABLE : batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
+ final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
- final double systemPowerMah = calculatePower(activityCounter);
+ final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter);
// Subtract what the apps used, but clamp to 0.
final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
@@ -91,11 +96,16 @@ public class BluetoothPowerCalculator extends PowerCalculator {
systemComponentPowerMah);
}
- private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total) {
- final BatteryStats.ControllerActivityCounter activityCounter =
+ private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
+ BatteryUsageStatsQuery query) {
+
+ final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ?
+ POWER_DATA_UNAVAILABLE :
+ app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
+ final ControllerActivityCounter activityCounter =
app.getBatteryStatsUid().getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePower(activityCounter);
+ final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter);
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah);
@@ -121,10 +131,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
}
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
- final BatteryStats.ControllerActivityCounter activityCounter =
+ final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
+ final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
- final double systemPowerMah = calculatePower(activityCounter);
final long systemDurationMs = calculateDuration(activityCounter);
+ final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter);
// Subtract what the apps used, but clamp to 0.
final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
@@ -152,10 +163,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
PowerAndDuration total) {
- final BatteryStats.ControllerActivityCounter activityCounter =
- u.getBluetoothControllerActivity();
+
+ final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
+ final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePower(activityCounter);
+ final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter);
app.bluetoothRunningTimeMs = durationMs;
app.bluetoothPowerMah = powerMah;
@@ -166,7 +178,7 @@ public class BluetoothPowerCalculator extends PowerCalculator {
total.powerMah += powerMah;
}
- private long calculateDuration(BatteryStats.ControllerActivityCounter counter) {
+ private long calculateDuration(ControllerActivityCounter counter) {
if (counter == null) {
return 0;
}
@@ -176,7 +188,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
+ counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
}
- private double calculatePower(BatteryStats.ControllerActivityCounter counter) {
+ /** Returns bluetooth power usage based on the best data available. */
+ private double calculatePowerMah(long measuredChargeUC, ControllerActivityCounter counter) {
+ if (measuredChargeUC != POWER_DATA_UNAVAILABLE) {
+ return uCtoMah(measuredChargeUC);
+ }
if (counter == null) {
return 0;
}
@@ -195,6 +211,11 @@ public class BluetoothPowerCalculator extends PowerCalculator {
counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
final long txTimeMs =
counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+ return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
+ }
+
+ /** Returns estimated bluetooth power usage based on usage times. */
+ public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
/ (1000 * 60 * 60);
}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 97f727ba72c5..b15543a26b4c 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -85,12 +85,14 @@ public class CpuPowerCalculator extends PowerCalculator {
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
- calculateApp(app, app.getBatteryStatsUid(), result);
+ calculateApp(app, app.getBatteryStatsUid(), query, result);
}
}
- private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
- calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
+ private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ BatteryUsageStatsQuery query, Result result) {
+ calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED,
+ query.shouldForceUsePowerProfileModel(), result);
app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
@@ -112,7 +114,7 @@ public class CpuPowerCalculator extends PowerCalculator {
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
- calculatePowerAndDuration(u, statsType, result);
+ calculatePowerAndDuration(u, statsType, false, result);
app.cpuPowerMah = result.powerMah;
app.cpuTimeMs = result.durationMs;
@@ -120,46 +122,16 @@ public class CpuPowerCalculator extends PowerCalculator {
app.packageWithHighestDrain = result.packageWithHighestDrain;
}
- private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) {
+ private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType,
+ boolean forceUsePowerProfileModel, Result result) {
long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
- // Constant battery drain when CPU is active
- double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
-
- // Additional per-cluster battery drain
- long[] cpuClusterTimes = u.getCpuClusterTimes();
- if (cpuClusterTimes != null) {
- if (cpuClusterTimes.length == mNumCpuClusters) {
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- double power = calculatePerCpuClusterPowerMah(cluster,
- cpuClusterTimes[cluster]);
- powerMah += power;
- if (DEBUG) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
- + " clusterTimeMs=" + cpuClusterTimes[cluster]
- + " power=" + formatCharge(power));
- }
- }
- } else {
- Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
- + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
- }
- }
-
- // Additional per-frequency battery drain
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
- for (int speed = 0; speed < speedsForCluster; speed++) {
- final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
- final double power = calculatePerCpuFreqPowerMah(cluster, speed,
- timeUs / 1000);
- if (DEBUG) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
- + speed + " timeUs=" + timeUs + " power="
- + formatCharge(power));
- }
- powerMah += power;
- }
+ final double powerMah;
+ final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
+ if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) {
+ powerMah = calculateUidModeledPowerMah(u, statsType);
+ } else {
+ powerMah = uCtoMah(consumptionUC);
}
if (DEBUG && (durationMs != 0 || powerMah != 0)) {
@@ -208,6 +180,48 @@ public class CpuPowerCalculator extends PowerCalculator {
result.packageWithHighestDrain = packageWithHighestDrain;
}
+ private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
+ // Constant battery drain when CPU is active
+ double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
+
+ // Additional per-cluster battery drain
+ long[] cpuClusterTimes = u.getCpuClusterTimes();
+ if (cpuClusterTimes != null) {
+ if (cpuClusterTimes.length == mNumCpuClusters) {
+ for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+ double power = calculatePerCpuClusterPowerMah(cluster,
+ cpuClusterTimes[cluster]);
+ powerMah += power;
+ if (DEBUG) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
+ + " clusterTimeMs=" + cpuClusterTimes[cluster]
+ + " power=" + formatCharge(power));
+ }
+ }
+ } else {
+ Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+ + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
+ }
+ }
+
+ // Additional per-frequency battery drain
+ for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+ final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
+ for (int speed = 0; speed < speedsForCluster; speed++) {
+ final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
+ final double power = calculatePerCpuFreqPowerMah(cluster, speed,
+ timeUs / 1000);
+ if (DEBUG) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ + speed + " timeUs=" + timeUs + " power="
+ + formatCharge(power));
+ }
+ powerMah += power;
+ }
+ }
+ return powerMah;
+ }
+
/**
* Calculates active CPU power consumption.
*
diff --git a/core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java b/core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java
new file mode 100644
index 000000000000..2ffff73f18cf
--- /dev/null
+++ b/core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.Process;
+
+/**
+ * Reads CPU usage statistics about a selected process identified by its cmdline.
+ *
+ * Handles finding the pid for the process and delegates CPU usage reading from the eBPF map to
+ * KernelSingleProcessCpuThreadReader. Exactly one long-lived instance of the process is expected.
+ * Otherwise, no statistics are returned.
+ *
+ * See also SystemServerCpuThreadReader.
+ */
+public final class SelectedProcessCpuThreadReader {
+ private final String[] mCmdline;
+
+ private int mPid;
+ private KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
+
+ public SelectedProcessCpuThreadReader(String cmdline) {
+ mCmdline = new String[] { cmdline };
+ }
+
+ /** Returns CPU times, per thread group, since tracking started. */
+ @Nullable
+ public KernelSingleProcessCpuThreadReader.ProcessCpuUsage readAbsolute() {
+ int[] pids = Process.getPidsForCommands(mCmdline);
+ if (pids == null || pids.length != 1) {
+ return null;
+ }
+ int pid = pids[0];
+ if (mPid == pid) {
+ return mKernelCpuThreadReader.getProcessCpuUsage();
+ }
+ mPid = pid;
+ mKernelCpuThreadReader = KernelSingleProcessCpuThreadReader.create(mPid);
+ mKernelCpuThreadReader.startTrackingThreadCpuTimes();
+ return null;
+ }
+}
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 791e9ad5ef9d..14cdb0890b25 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -1,7 +1,11 @@
{
"presubmit": [
{
- "file_patterns": ["Battery[^/]*\\.java"],
+ "file_patterns": [
+ "Battery[^/]*\\.java",
+ "Kernel[^/]*\\.java",
+ "[^/]*Power[^/]*\\.java"
+ ],
"name": "FrameworksCoreTests",
"options": [
{ "include-filter": "com.android.internal.os.BatteryStatsTests" },
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 98f613fc1c40..b6bfde709cb8 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.os;
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
@@ -79,11 +81,6 @@ public class WifiPowerCalculator extends PowerCalculator {
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
- // so always check this field.
- final boolean hasWifiPowerReporting =
- mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
-
final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
builder.getOrCreateSystemBatteryConsumerBuilder(
SystemBatteryConsumer.DRAIN_TYPE_WIFI);
@@ -97,7 +94,8 @@ public class WifiPowerCalculator extends PowerCalculator {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED,
- hasWifiPowerReporting);
+ batteryStats.hasWifiActivityReporting(),
+ query.shouldForceUsePowerProfileModel());
totalAppDurationMs += powerDurationAndTraffic.durationMs;
totalAppPowerMah += powerDurationAndTraffic.powerMah;
@@ -115,7 +113,9 @@ public class WifiPowerCalculator extends PowerCalculator {
calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED,
- hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+ batteryStats.hasWifiActivityReporting(),
+ query.shouldForceUsePowerProfileModel(),
+ totalAppDurationMs, totalAppPowerMah);
systemBatteryConsumerBuilder
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
@@ -135,11 +135,6 @@ public class WifiPowerCalculator extends PowerCalculator {
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
- // so always check this field.
- final boolean hasWifiPowerReporting =
- mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
-
final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
long totalAppDurationMs = 0;
@@ -149,7 +144,7 @@ public class WifiPowerCalculator extends PowerCalculator {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType,
- hasWifiPowerReporting);
+ batteryStats.hasWifiActivityReporting(), /* force use power model*/ false);
totalAppDurationMs += powerDurationAndTraffic.durationMs;
totalAppPowerMah += powerDurationAndTraffic.powerMah;
@@ -169,7 +164,8 @@ public class WifiPowerCalculator extends PowerCalculator {
}
calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType,
- hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+ batteryStats.hasWifiActivityReporting(), /* force use power model*/ false,
+ totalAppDurationMs, totalAppPowerMah);
bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
bs.wifiPowerMah += powerDurationAndTraffic.powerMah;
@@ -180,8 +176,9 @@ public class WifiPowerCalculator extends PowerCalculator {
}
private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u,
- long rawRealtimeUs,
- int statsType, boolean hasWifiPowerReporting) {
+ long rawRealtimeUs, int statsType,
+ boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel) {
+
powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
BatteryStats.NETWORK_WIFI_RX_DATA,
statsType);
@@ -195,7 +192,14 @@ public class WifiPowerCalculator extends PowerCalculator {
BatteryStats.NETWORK_WIFI_TX_DATA,
statsType);
- if (hasWifiPowerReporting) {
+ final long measuredChargeUC = u.getWifiMeasuredBatteryConsumptionUC();
+ final boolean isMeasuredPowerAvailable
+ = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE;
+ if (isMeasuredPowerAvailable) {
+ powerDurationAndTraffic.powerMah = uCtoMah(measuredChargeUC);
+ }
+
+ if (hasWifiActivityReporting && mHasWifiPowerController) {
final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
if (counter != null) {
final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
@@ -203,9 +207,10 @@ public class WifiPowerCalculator extends PowerCalculator {
final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
- powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime)
- + mTxPowerEstimator.calculatePower(txTime)
- + mRxPowerEstimator.calculatePower(rxTime);
+ if (!isMeasuredPowerAvailable) {
+ powerDurationAndTraffic.powerMah
+ = calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
+ }
if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
@@ -214,21 +219,20 @@ public class WifiPowerCalculator extends PowerCalculator {
}
}
} else {
- final double wifiPacketPower = (
- powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets)
- * mWifiPowerPerPacket;
final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
- final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
- long batchScanTimeMs = 0;
- for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
- batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
- }
-
powerDurationAndTraffic.durationMs = wifiRunningTime;
- powerDurationAndTraffic.powerMah = wifiPacketPower
- + mPowerOnPowerEstimator.calculatePower(wifiRunningTime)
- + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
- + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs);
+
+ if (!isMeasuredPowerAvailable) {
+ final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
+ long batchTimeMs = 0;
+ for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+ batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
+ }
+ powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah(
+ powerDurationAndTraffic.wifiRxPackets,
+ powerDurationAndTraffic.wifiTxPackets,
+ wifiRunningTime, wifiScanTimeMs, batchTimeMs);
+ }
if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
@@ -238,12 +242,20 @@ public class WifiPowerCalculator extends PowerCalculator {
}
private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
- BatteryStats stats, long rawRealtimeUs,
- int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs,
- double totalAppPowerMah) {
+ BatteryStats stats, long rawRealtimeUs, int statsType,
+ boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel,
+ long totalAppDurationMs, double totalAppPowerMah) {
+
long totalDurationMs;
- double totalPowerMah;
- if (hasWifiPowerReporting) {
+ double totalPowerMah = 0;
+
+ final long measuredChargeUC = stats.getWifiMeasuredBatteryConsumptionUC();
+ final boolean isMeasuredPowerAvailable
+ = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE;
+ if (isMeasuredPowerAvailable) {
+ totalPowerMah = uCtoMah(measuredChargeUC);
+ }
+ if (hasWifiActivityReporting && mHasWifiPowerController) {
final BatteryStats.ControllerActivityCounter counter =
stats.getWifiControllerActivity();
@@ -253,17 +265,19 @@ public class WifiPowerCalculator extends PowerCalculator {
totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;
- totalPowerMah =
- counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60);
- if (totalPowerMah == 0) {
- // Some controllers do not report power drain, so we can calculate it here.
- totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs)
- + mTxPowerEstimator.calculatePower(txTimeMs)
- + mRxPowerEstimator.calculatePower(rxTimeMs);
+ if (!isMeasuredPowerAvailable) {
+ totalPowerMah = counter.getPowerCounter().getCountLocked(statsType)
+ / (double) (1000 * 60 * 60);
+ if (totalPowerMah == 0) {
+ // Some controllers do not report power drain, so we can calculate it here.
+ totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs);
+ }
}
} else {
totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
- totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs);
+ if (!isMeasuredPowerAvailable) {
+ totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs);
+ }
}
powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
@@ -274,6 +288,29 @@ public class WifiPowerCalculator extends PowerCalculator {
}
}
+ /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */
+ public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
+ return mRxPowerEstimator.calculatePower(rxTimeMs)
+ + mTxPowerEstimator.calculatePower(txTimeMs)
+ + mIdlePowerEstimator.calculatePower(idleTimeMs);
+ }
+
+ /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */
+ public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets,
+ long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) {
+ return
+ (rxPackets + txPackets) * mWifiPowerPerPacket
+ + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs)
+ + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
+ + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs);
+
+ }
+
+ /** Returns global estimated wifi power used using non-WifiControllerActivity data. */
+ public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) {
+ return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs);
+ }
+
/**
* Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
*/
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 47b0f8c2be0c..2458fe3dfb8c 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -41,7 +41,6 @@ import android.os.UserHandle;
import android.os.ZygoteProcess;
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -74,7 +73,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Provider;
import java.security.Security;
-import java.util.Optional;
/**
* Startup class for the zygote process.
@@ -227,17 +225,7 @@ public class ZygoteInit {
// AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert
// preferred providers. Note this is not done via security.properties as the JCA providers
// are not on the classpath in the case of, for example, raw dalvikvm runtimes.
- // TODO b/171305684 This code is used to conditionally enable the installation of the
- // Keystore 2.0 provider to enable teams adjusting to Keystore 2.0 at their own
- // pace. This code will be removed when all calling code was adjusted to
- // Keystore 2.0.
- Optional<Boolean> keystore2_enabled =
- android.sysprop.Keystore2Properties.keystore2_enabled();
- if (keystore2_enabled.isPresent() && keystore2_enabled.get()) {
- android.security.keystore2.AndroidKeyStoreProvider.install();
- } else {
- AndroidKeyStoreProvider.install();
- }
+ android.security.keystore2.AndroidKeyStoreProvider.install();
Log.i(TAG, "Installed AndroidKeyStoreProvider in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 6049486b380c..39bde742e828 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -78,7 +78,7 @@ import android.view.ContextThemeWrapper;
import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
import android.view.IRotationWatcher.Stub;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -3940,12 +3940,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
/**
* System request to begin scroll capture.
*
- * @param callbacks to receive responses
+ * @param listener to receive the response
* @hide
*/
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
- getViewRootImpl().dispatchScrollCaptureRequest(callbacks);
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
+ getViewRootImpl().dispatchScrollCaptureRequest(listener);
}
/**
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index e3d5464ca413..845b3e501c08 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -52,7 +52,9 @@ public class MeasuredEnergyStats {
public static final int POWER_BUCKET_SCREEN_DOZE = 1;
public static final int POWER_BUCKET_SCREEN_OTHER = 2;
public static final int POWER_BUCKET_CPU = 3;
- public static final int NUMBER_STANDARD_POWER_BUCKETS = 4; // Buckets above this are custom.
+ public static final int POWER_BUCKET_WIFI = 4;
+ public static final int POWER_BUCKET_BLUETOOTH = 5;
+ public static final int NUMBER_STANDARD_POWER_BUCKETS = 6; // Buckets above this are custom.
@IntDef(prefix = {"POWER_BUCKET_"}, value = {
POWER_BUCKET_UNKNOWN,
@@ -60,6 +62,8 @@ public class MeasuredEnergyStats {
POWER_BUCKET_SCREEN_DOZE,
POWER_BUCKET_SCREEN_OTHER,
POWER_BUCKET_CPU,
+ POWER_BUCKET_WIFI,
+ POWER_BUCKET_BLUETOOTH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface StandardPowerBucket {
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
index bed85aed3625..83309fc61009 100644
--- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -359,18 +359,6 @@ public class BaseProtoLogImpl {
+ "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
}
- /**
- * Writes the log buffer to a new file for the bugreport.
- *
- * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
- * {@link #stopProtoLog(PrintWriter, boolean)}.
- */
- public void writeProtoLogToFile() {
- synchronized (mProtoLogEnabledLock) {
- writeProtoLogToFileLocked();
- }
- }
-
private void writeProtoLogToFileLocked() {
try {
long offset =
diff --git a/core/java/com/android/internal/security/OWNERS b/core/java/com/android/internal/security/OWNERS
new file mode 100644
index 000000000000..41d1d6687c42
--- /dev/null
+++ b/core/java/com/android/internal/security/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+per-file VerityUtils.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING
index 9a5e90e8681f..9a5e90e8681f 100644
--- a/services/core/java/com/android/server/security/TEST_MAPPING
+++ b/core/java/com/android/internal/security/TEST_MAPPING
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/core/java/com/android/internal/security/VerityUtils.java
index 48a60387fee7..ef703a996001 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/core/java/com/android/internal/security/VerityUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.security;
+package com.android.internal.security;
import android.annotation.NonNull;
import android.os.Build;
@@ -42,7 +42,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/** Provides fsverity related operations. */
-abstract public class VerityUtils {
+public abstract class VerityUtils {
private static final String TAG = "VerityUtils";
/**
@@ -156,8 +156,8 @@ abstract public class VerityUtils {
return SetupResult.failed();
}
return SetupResult.ok(Os.dup(rfd), contentSize);
- } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException |
- SignatureNotFoundException | ErrnoException e) {
+ } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException
+ | SignatureNotFoundException | ErrnoException e) {
Slog.e(TAG, "Failed to set up apk verity: ", e);
return SetupResult.failed();
} finally {
@@ -243,14 +243,20 @@ abstract public class VerityUtils {
private final FileDescriptor mFileDescriptor;
private final int mContentSize;
+ /** @deprecated */
+ @Deprecated
public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) {
return new SetupResult(RESULT_OK, fileDescriptor, contentSize);
}
+ /** @deprecated */
+ @Deprecated
public static SetupResult skipped() {
return new SetupResult(RESULT_SKIPPED, null, -1);
}
+ /** @deprecated */
+ @Deprecated
public static SetupResult failed() {
return new SetupResult(RESULT_FAILED, null, -1);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 200e0dd6e65b..fea07519fb4d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -257,4 +257,12 @@ oneway interface IStatusBar
* file descriptor passed in.
*/
void passThroughShellCommand(in String[] args, in ParcelFileDescriptor pfd);
+
+ /**
+ * Enables/disables the navigation bar luma sampling.
+ *
+ * @param displayId the id of the display to notify.
+ * @param enable {@code true} if enable, otherwise set to {@code false}.
+ */
+ void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
}
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 85114e59cc2d..f3d085814baa 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,6 +21,7 @@ import android.telephony.CallAttributes;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
@@ -72,5 +73,6 @@ oneway interface IPhoneStateListener {
void onBarringInfoChanged(in BarringInfo barringInfo);
void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
void onDataEnabledChanged(boolean enabled, int reason);
- void onAllowedNetworkTypesChanged(in Map allowedNetworkTypeList);
+ void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
+ void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList);
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 95e0a3b524c5..34187687c4e8 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -23,6 +23,7 @@ import android.telephony.BarringInfo;
import android.telephony.CallQuality;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.PhoneCapability;
@@ -95,5 +96,7 @@ interface ITelephonyRegistry {
void notifyPhysicalChannelConfigForSubscriber(in int subId,
in List<PhysicalChannelConfig> configs);
void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
- void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList);
+ void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
+ void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId,
+ in List<LinkCapacityEstimate> linkCapacityEstimateList);
}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index dc6880e4f997..90c728293eb0 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -175,7 +175,7 @@ public class LatencyTracker {
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
for (int action : ACTIONS_ALL) {
mTraceThresholdPerAction[action] =
- properties.getInt(getTraceTriggerNameForAction(action), -1);
+ properties.getInt(getNameOfAction(STATSD_ACTION[action]), -1);
}
}
}
diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java
deleted file mode 100644
index d67bd7a853c8..000000000000
--- a/core/java/com/android/internal/util/LocationPermissionChecker.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.net.NetworkStack;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-
-/**
- * The methods used for location permission and location mode checking.
- *
- * @hide
- */
-public class LocationPermissionChecker {
-
- private static final String TAG = "LocationPermissionChecker";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"LOCATION_PERMISSION_CHECK_STATUS_"}, value = {
- SUCCEEDED,
- ERROR_LOCATION_MODE_OFF,
- ERROR_LOCATION_PERMISSION_MISSING,
- })
- public @interface LocationPermissionCheckStatus{}
-
- // The location permission check succeeded.
- public static final int SUCCEEDED = 0;
- // The location mode turns off for the caller.
- public static final int ERROR_LOCATION_MODE_OFF = 1;
- // The location permission isn't granted for the caller.
- public static final int ERROR_LOCATION_PERMISSION_MISSING = 2;
-
- private final Context mContext;
- private final AppOpsManager mAppOpsManager;
-
- public LocationPermissionChecker(Context context) {
- mContext = context;
- mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- }
-
- /**
- * Check location permission granted by the caller.
- *
- * This API check if the location mode enabled for the caller and the caller has
- * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
- *
- * @param pkgName package name of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- *
- * @return {@code true} returns if the caller has location permission and the location mode is
- * enabled.
- */
- public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
- int uid, @Nullable String message) {
- return checkLocationPermissionInternal(pkgName, featureId, uid, message) == SUCCEEDED;
- }
-
- /**
- * Check location permission granted by the caller.
- *
- * This API check if the location mode enabled for the caller and the caller has
- * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
- * Compared with {@link #checkLocationPermission(String, String, int, String)}, this API returns
- * the detail information about the checking result, including the reason why it's failed and
- * logs the error for the caller.
- *
- * @param pkgName package name of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- *
- * @return {@link LocationPermissionCheckStatus} the result of the location permission check.
- */
- public @LocationPermissionCheckStatus int checkLocationPermissionWithDetailInfo(
- String pkgName, @Nullable String featureId, int uid, @Nullable String message) {
- final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
- switch (result) {
- case ERROR_LOCATION_MODE_OFF:
- Log.e(TAG, "Location mode is disabled for the device");
- break;
- case ERROR_LOCATION_PERMISSION_MISSING:
- Log.e(TAG, "UID " + uid + " has no location permission");
- break;
- }
- return result;
- }
-
- /**
- * Enforce the caller has location permission.
- *
- * This API determines if the location mode enabled for the caller and the caller has
- * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
- * SecurityException is thrown if the caller has no permission or the location mode is disabled.
- *
- * @param pkgName package name of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- */
- public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
- @Nullable String message) throws SecurityException {
- final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
-
- switch (result) {
- case ERROR_LOCATION_MODE_OFF:
- throw new SecurityException("Location mode is disabled for the device");
- case ERROR_LOCATION_PERMISSION_MISSING:
- throw new SecurityException("UID " + uid + " has no location permission");
- }
- }
-
- private int checkLocationPermissionInternal(String pkgName, @Nullable String featureId,
- int uid, @Nullable String message) {
- checkPackage(uid, pkgName);
-
- // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK
- // are granted a bypass.
- if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
- || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
- return SUCCEEDED;
- }
-
- // Location mode must be enabled
- if (!isLocationModeEnabled()) {
- return ERROR_LOCATION_MODE_OFF;
- }
-
- // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
- // location information.
- if (!checkCallersLocationPermission(pkgName, featureId, uid,
- true /* coarseForTargetSdkLessThanQ */, message)) {
- return ERROR_LOCATION_PERMISSION_MISSING;
- }
- return SUCCEEDED;
- }
-
- /**
- * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
- * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
- * and a corresponding app op is allowed for this package and uid.
- *
- * @param pkgName PackageName of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
- * else (false or targetSDK >= Q) then will check for FINE
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- */
- public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
- int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
-
- boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
-
- String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
- if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
- // Having FINE permission implies having COARSE permission (but not the reverse)
- permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
- }
- if (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) {
- return false;
- }
-
- // Always checking FINE - even if will not enforce. This will record the request for FINE
- // so that a location request by the app is surfaced to the user.
- boolean isFineLocationAllowed = noteAppOpAllowed(
- AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
- if (isFineLocationAllowed) {
- return true;
- }
- if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
- return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
- message);
- }
- return false;
- }
-
- /**
- * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
- */
- public boolean isLocationModeEnabled() {
- final LocationManager LocationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- try {
- return LocationManager.isLocationEnabledForUser(UserHandle.of(
- getCurrentUser()));
- } catch (Exception e) {
- Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
- return false;
- }
- }
-
- private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mContext.getPackageManager().getApplicationInfoAsUser(
- packageName, 0,
- UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
- < versionCode) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume unknown app (more strict checking)
- // Note: This case will never happen since checkPackage is
- // called to verify validity before checking App's version.
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return false;
- }
-
- private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
- int uid, @Nullable String message) {
- return mAppOpsManager.noteOp(op, uid, pkgName, featureId, message)
- == AppOpsManager.MODE_ALLOWED;
- }
-
- private void checkPackage(int uid, String pkgName)
- throws SecurityException {
- if (pkgName == null) {
- throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
- }
- mAppOpsManager.checkPackage(uid, pkgName);
- }
-
- @VisibleForTesting
- protected int getCurrentUser() {
- return ActivityManager.getCurrentUser();
- }
-
- private int getUidPermission(String permissionType, int uid) {
- // We don't care about pid, pass in -1
- return mContext.checkPermission(permissionType, -1, uid);
- }
-
- /**
- * Returns true if the |uid| holds NETWORK_SETTINGS permission.
- */
- public boolean checkNetworkSettingsPermission(int uid) {
- return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
- */
- public boolean checkNetworkSetupWizardPermission(int uid) {
- return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the |uid| holds NETWORK_STACK permission.
- */
- public boolean checkNetworkStackPermission(int uid) {
- return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
- */
- public boolean checkMainlineNetworkStackPermission(int uid) {
- return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
-}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 276cad9f6d6d..bbd738bc9eb6 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -59,8 +59,6 @@ public class Protocol {
public static final int BASE_TETHERING = 0x00050000;
public static final int BASE_NSD_MANAGER = 0x00060000;
public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000;
- public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000;
- public static final int BASE_NETWORK_AGENT = 0x00081000;
public static final int BASE_NETWORK_FACTORY = 0x00083000;
public static final int BASE_ETHERNET = 0x00084000;
public static final int BASE_LOWPAN = 0x00085000;
diff --git a/core/java/com/android/internal/util/ScreenRecordHelper.java b/core/java/com/android/internal/util/ScreenRecordHelper.java
deleted file mode 100644
index ec7ed4e0008a..000000000000
--- a/core/java/com/android/internal/util/ScreenRecordHelper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * Helper class to initiate a screen recording
- */
-public class ScreenRecordHelper {
- private final Context mContext;
-
- /**
- * Create a new ScreenRecordHelper for the given context
- * @param context
- */
- public ScreenRecordHelper(Context context) {
- mContext = context;
- }
-
- /**
- * Show dialog of screen recording options to user.
- */
- public void launchRecordPrompt() {
- final ComponentName launcherComponent = ComponentName.unflattenFromString(
- mContext.getResources().getString(
- com.android.internal.R.string.config_screenRecorderComponent));
- final Intent intent = new Intent();
- intent.setComponent(launcherComponent);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
-}
diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java
index 413be484dc32..131114cad460 100644
--- a/core/java/com/android/internal/util/TrafficStatsConstants.java
+++ b/core/java/com/android/internal/util/TrafficStatsConstants.java
@@ -21,24 +21,8 @@ package com.android.internal.util;
* @hide
*/
public class TrafficStatsConstants {
- // These tags are used by the network stack to do traffic for its own purposes. Traffic
- // tagged with these will be counted toward the network stack and must stay inside the
- // range defined by
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}.
- public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01;
- public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02;
- public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03;
public static final int TAG_SYSTEM_NTP = 0xFFFFFF41;
public static final int TAG_SYSTEM_GPS = 0xFFFFFF44;
public static final int TAG_SYSTEM_PAC = 0xFFFFFF45;
-
- // These tags are used by the network stack to do traffic on behalf of apps. Traffic
- // tagged with these will be counted toward the app on behalf of which the network
- // stack is doing this traffic. These values must stay inside the range defined by
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}.
- public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81;
- public static final int TAG_SYSTEM_DNS = 0xFFFFFF82;
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index ab0149fce0a0..47341cd154d7 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -24,7 +24,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.MergedConfiguration;
import android.view.DragEvent;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.InsetsSourceControl;
@@ -159,9 +159,9 @@ public class BaseIWindow extends IWindow.Stub {
}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
try {
- callbacks.onScrollCaptureResponse(
+ listener.onScrollCaptureResponse(
new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build());
} catch (RemoteException ex) {
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 19506a325d9a..783d088b19b9 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -31,8 +31,10 @@ import android.util.imetracing.ImeTracing;
import android.util.imetracing.InputConnectionHelper;
import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
+import android.view.View;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DumpableInputConnection;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
@@ -49,7 +51,9 @@ import com.android.internal.inputmethod.IIntResultCallback;
import com.android.internal.inputmethod.ISurroundingTextResultCallback;
import com.android.internal.os.SomeArgs;
-public abstract class IInputConnectionWrapper extends IInputContext.Stub {
+import java.lang.ref.WeakReference;
+
+public final class IInputConnectionWrapper extends IInputContext.Stub {
private static final String TAG = "IInputConnectionWrapper";
private static final boolean DEBUG = false;
@@ -90,10 +94,13 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private Looper mMainLooper;
private Handler mH;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private Object mLock = new Object();
+ private final Object mLock = new Object();
@GuardedBy("mLock")
private boolean mFinished = false;
+ private final InputMethodManager mParentInputMethodManager;
+ private final WeakReference<View> mServedView;
+
class MyHandler extends Handler {
MyHandler(Looper looper) {
super(looper);
@@ -104,11 +111,15 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
executeMessage(msg);
}
}
-
- public IInputConnectionWrapper(Looper mainLooper, @NonNull InputConnection inputConnection) {
+
+ public IInputConnectionWrapper(@NonNull Looper mainLooper,
+ @NonNull InputConnection inputConnection,
+ @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
mInputConnection = inputConnection;
mMainLooper = mainLooper;
mH = new MyHandler(mMainLooper);
+ mParentInputMethodManager = inputMethodManager;
+ mServedView = new WeakReference<>(servedView);
}
@Nullable
@@ -118,21 +129,70 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
}
}
- protected Looper getLooper() {
- synchronized (mMainLooper) {
- return mMainLooper;
+ private boolean isFinished() {
+ synchronized (mLock) {
+ return mFinished;
}
}
- protected boolean isFinished() {
- synchronized (mLock) {
- return mFinished;
+ public boolean isActive() {
+ return mParentInputMethodManager.isActive() && !isFinished();
+ }
+
+ public View getServedView() {
+ return mServedView.get();
+ }
+
+ public void deactivate() {
+ if (isFinished()) {
+ // This is a small performance optimization. Still only the 1st call of
+ // reportFinish() will take effect.
+ return;
+ }
+ closeConnection();
+
+ // Notify the app that the InputConnection was closed.
+ final View servedView = mServedView.get();
+ if (servedView != null) {
+ final Handler handler = servedView.getHandler();
+ // The handler is null if the view is already detached. When that's the case, for
+ // now, we simply don't dispatch this callback.
+ if (handler != null) {
+ if (DEBUG) {
+ Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
+ }
+ if (handler.getLooper().isCurrentThread()) {
+ servedView.onInputConnectionClosedInternal();
+ } else {
+ handler.post(servedView::onInputConnectionClosedInternal);
+ }
+ }
}
}
- protected abstract boolean isActive();
+ @Override
+ public String toString() {
+ return "IInputConnectionWrapper{"
+ + "connection=" + getInputConnection()
+ + " finished=" + isFinished()
+ + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+ + " mServedView=" + mServedView.get()
+ + "}";
+ }
- protected abstract InputMethodManager getIMM();
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ synchronized (mLock) {
+ // Check that the call is initiated in the main thread of the current InputConnection
+ // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+ // executed on this thread. Otherwise the messages are dispatched to the correct thread
+ // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
+ // reasons.
+ if ((mInputConnection instanceof DumpableInputConnection)
+ && Looper.myLooper() == mMainLooper) {
+ ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
+ }
+ }
+ }
public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
@@ -309,7 +369,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
msg.arg2, result);
ImeTracing.getInstance().triggerClientDump(
- TAG + "#getTextAfterCursor", getIMM(), icProto);
+ TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
}
try {
callback.onResult(result);
@@ -339,7 +399,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
msg.arg2, result);
ImeTracing.getInstance().triggerClientDump(
- TAG + "#getTextBeforeCursor", getIMM(), icProto);
+ TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
}
try {
callback.onResult(result);
@@ -368,7 +428,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
if (ImeTracing.getInstance().isEnabled()) {
icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
ImeTracing.getInstance().triggerClientDump(
- TAG + "#getSelectedText", getIMM(), icProto);
+ TAG + "#getSelectedText", mParentInputMethodManager, icProto);
}
try {
callback.onResult(result);
@@ -402,7 +462,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
afterLength, flags, result);
ImeTracing.getInstance().triggerClientDump(
- TAG + "#getSurroundingText", getIMM(), icProto);
+ TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
}
try {
callback.onResult(result);
@@ -432,7 +492,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
result);
ImeTracing.getInstance().triggerClientDump(
- TAG + "#getCursorCapsMode", getIMM(), icProto);
+ TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
}
try {
callback.onResult(result);
@@ -464,7 +524,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
msg.arg1, result);
ImeTracing.getInstance().triggerClientDump(
- TAG + "#getExtractedText", getIMM(), icProto);
+ TAG + "#getExtractedText", mParentInputMethodManager, icProto);
}
try {
callback.onResult(result);
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index f29e95ccdf65..c9755a3dcedc 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -204,6 +204,8 @@ public final class InputBindResult implements Parcelable {
@Nullable
private final float[] mActivityViewToScreenMatrixValues;
+ public final boolean isInputMethodSuppressingSpellChecker;
+
/**
* @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}.
* {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}.
@@ -220,7 +222,8 @@ public final class InputBindResult implements Parcelable {
public InputBindResult(@ResultCode int _result,
IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
- @Nullable Matrix activityViewToScreenMatrix) {
+ @Nullable Matrix activityViewToScreenMatrix,
+ boolean isInputMethodSuppressingSpellChecker) {
result = _result;
method = _method;
channel = _channel;
@@ -232,6 +235,7 @@ public final class InputBindResult implements Parcelable {
mActivityViewToScreenMatrixValues = new float[9];
activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
}
+ this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker;
}
InputBindResult(Parcel source) {
@@ -245,6 +249,7 @@ public final class InputBindResult implements Parcelable {
id = source.readString();
sequence = source.readInt();
mActivityViewToScreenMatrixValues = source.createFloatArray();
+ isInputMethodSuppressingSpellChecker = source.readBoolean();
}
@Override
@@ -252,6 +257,7 @@ public final class InputBindResult implements Parcelable {
return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ " sequence=" + sequence
+ " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
+ + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
+ "}";
}
@@ -274,6 +280,7 @@ public final class InputBindResult implements Parcelable {
dest.writeString(id);
dest.writeInt(sequence);
dest.writeFloatArray(mActivityViewToScreenMatrixValues);
+ dest.writeBoolean(isInputMethodSuppressingSpellChecker);
}
/**
@@ -340,7 +347,7 @@ public final class InputBindResult implements Parcelable {
}
private static InputBindResult error(@ResultCode int result) {
- return new InputBindResult(result, null, null, null, -1, null);
+ return new InputBindResult(result, null, null, null, -1, null, false);
}
/**
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
index 851d1f37522a..eb2478f6550a 100644
--- a/core/java/com/android/internal/view/OWNERS
+++ b/core/java/com/android/internal/view/OWNERS
@@ -18,3 +18,7 @@ per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNER
per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/com/android/internal/view/inline/InlineTooltipUi.java b/core/java/com/android/internal/view/inline/InlineTooltipUi.java
new file mode 100644
index 000000000000..5ec8b30d6a7b
--- /dev/null
+++ b/core/java/com/android/internal/view/inline/InlineTooltipUi.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.view.inline;
+
+import static android.view.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.graphics.drawable.Drawable;
+import android.transition.Transition;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.inline.InlineContentView;
+
+import java.io.PrintWriter;
+
+/**
+ * UI container for the inline suggestion tooltip.
+ */
+public final class InlineTooltipUi extends PopupWindow implements AutoCloseable {
+ private static final String TAG = "InlineTooltipUi";
+
+ private final WindowManager mWm;
+ private final ViewGroup mContentContainer;
+
+ private boolean mShowing;
+
+ private WindowManager.LayoutParams mWindowLayoutParams;
+
+ private final View.OnAttachStateChangeListener mAnchorOnAttachStateChangeListener =
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ /* ignore - handled by the super class */
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ dismiss();
+ }
+ };
+
+ private final View.OnLayoutChangeListener mAnchoredOnLayoutChangeListener =
+ new View.OnLayoutChangeListener() {
+ int mHeight;
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mHeight != bottom - top) {
+ mHeight = bottom - top;
+ adjustPosition();
+ }
+ }
+ };
+
+ public InlineTooltipUi(@NonNull Context context) {
+ mContentContainer = new LinearLayout(new ContextWrapper(context));
+ mWm = context.getSystemService(WindowManager.class);
+
+ setTouchModal(false);
+ setOutsideTouchable(true);
+ setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
+ setFocusable(false);
+ }
+
+ /**
+ * Sets the content view for inline suggestions tooltip
+ * @param v the content view of {@link android.widget.inline.InlineContentView}
+ */
+ public void setTooltipView(@NonNull InlineContentView v) {
+ mContentContainer.removeAllViews();
+ mContentContainer.addView(v);
+ mContentContainer.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void close() {
+ hide();
+ }
+
+ @Override
+ protected boolean hasContentView() {
+ return true;
+ }
+
+ @Override
+ protected boolean hasDecorView() {
+ return true;
+ }
+
+ @Override
+ protected WindowManager.LayoutParams getDecorViewLayoutParams() {
+ return mWindowLayoutParams;
+ }
+
+ /**
+ * The effective {@code update} method that should be called by its clients.
+ */
+ public void update(View anchor) {
+ // set to the application type with the highest z-order
+ setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+
+ // The first time to show up, the height of tooltip is zero,
+ // so set the offset Y to 2 * anchor height.
+ final int achoredHeight = mContentContainer.getHeight();
+ final int offsetY = (achoredHeight == 0)
+ ? -anchor.getHeight() << 1 : -anchor.getHeight() - achoredHeight;
+ if (!isShowing()) {
+ setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
+ setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
+ showAsDropDown(anchor, 0 , offsetY, Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ } else {
+ update(anchor, 0 , offsetY, WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ @Override
+ protected void update(View anchor, WindowManager.LayoutParams params) {
+ // update content view for the anchor is scrolling
+ if (anchor.isVisibleToUser()) {
+ show(params);
+ } else {
+ hide();
+ }
+ }
+
+ @Override
+ public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
+ if (isShowing()) {
+ return;
+ }
+
+ setShowing(true);
+ setDropDown(true);
+ attachToAnchor(anchor, xoff, yoff, gravity);
+ final WindowManager.LayoutParams p = mWindowLayoutParams = createPopupLayoutParams(
+ anchor.getWindowToken());
+ final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
+ p.width, p.height, gravity, getAllowScrollingAnchorParent());
+ updateAboveAnchor(aboveAnchor);
+ p.accessibilityIdOfAnchor = anchor.getAccessibilityViewId();
+ p.packageName = anchor.getContext().getPackageName();
+ show(p);
+ }
+
+ @Override
+ protected void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
+ super.attachToAnchor(anchor, xoff, yoff, gravity);
+ anchor.addOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener);
+ }
+
+ @Override
+ protected void detachFromAnchor() {
+ final View anchor = getAnchor();
+ if (anchor != null) {
+ anchor.removeOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener);
+ }
+ super.detachFromAnchor();
+ }
+
+ @Override
+ public void dismiss() {
+ if (!isShowing() || isTransitioningToDismiss()) {
+ return;
+ }
+
+ setShowing(false);
+ setTransitioningToDismiss(true);
+
+ hide();
+ detachFromAnchor();
+ if (getOnDismissListener() != null) {
+ getOnDismissListener().onDismiss();
+ }
+ }
+
+ private void adjustPosition() {
+ View anchor = getAnchor();
+ if (anchor == null) return;
+ update(anchor);
+ }
+
+ private void show(WindowManager.LayoutParams params) {
+ if (sVerbose) {
+ Slog.v(TAG, "show()");
+ }
+ mWindowLayoutParams = params;
+
+ try {
+ params.packageName = "android";
+ params.setTitle("Autofill Inline Tooltip"); // Title is set for debugging purposes
+ if (!mShowing) {
+ params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mContentContainer.addOnLayoutChangeListener(mAnchoredOnLayoutChangeListener);
+ mWm.addView(mContentContainer, params);
+ mShowing = true;
+ } else {
+ mWm.updateViewLayout(mContentContainer, params);
+ }
+ } catch (WindowManager.BadTokenException e) {
+ Slog.d(TAG, "Failed with token " + params.token + " gone.");
+ } catch (IllegalStateException e) {
+ // WM throws an ISE if mContentView was added twice; this should never happen -
+ // since show() and hide() are always called in the UIThread - but when it does,
+ // it should not crash the system.
+ Slog.wtf(TAG, "Exception showing window " + params, e);
+ }
+ }
+
+ private void hide() {
+ if (sVerbose) {
+ Slog.v(TAG, "hide()");
+ }
+ try {
+ if (mShowing) {
+ mContentContainer.removeOnLayoutChangeListener(mAnchoredOnLayoutChangeListener);
+ mWm.removeView(mContentContainer);
+ mShowing = false;
+ }
+ } catch (IllegalStateException e) {
+ // WM might thrown an ISE when removing the mContentView; this should never
+ // happen - since show() and hide() are always called in the UIThread - but if it
+ // does, it should not crash the system.
+ Slog.e(TAG, "Exception hiding window ", e);
+ }
+ }
+
+ @Override
+ public int getAnimationStyle() {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public Drawable getBackground() {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public View getContentView() {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public float getElevation() {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public Transition getEnterTransition() {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public Transition getExitTransition() {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public void setBackgroundDrawable(Drawable background) {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public void setContentView(View contentView) {
+ if (contentView != null) {
+ throw new IllegalStateException("You can't call this!");
+ }
+ }
+
+ @Override
+ public void setElevation(float elevation) {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public void setEnterTransition(Transition enterTransition) {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public void setExitTransition(Transition exitTransition) {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ @Override
+ public void setTouchInterceptor(View.OnTouchListener l) {
+ throw new IllegalStateException("You can't call this!");
+ }
+
+ /**
+ * Dumps status
+ */
+ public void dump(@NonNull PrintWriter pw, @Nullable String prefix) {
+
+ pw.print(prefix);
+
+ if (mContentContainer != null) {
+ pw.print(prefix); pw.print("Window: ");
+ final String prefix2 = prefix + " ";
+ pw.println();
+ pw.print(prefix2); pw.print("showing: "); pw.println(mShowing);
+ pw.print(prefix2); pw.print("view: "); pw.println(mContentContainer);
+ if (mWindowLayoutParams != null) {
+ pw.print(prefix2); pw.print("params: "); pw.println(mWindowLayoutParams);
+ }
+ pw.print(prefix2); pw.print("screen coordinates: ");
+ if (mContentContainer == null) {
+ pw.println("N/A");
+ } else {
+ final int[] coordinates = mContentContainer.getLocationOnScreen();
+ pw.print(coordinates[0]); pw.print("x"); pw.println(coordinates[1]);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/inline/OWNERS b/core/java/com/android/internal/view/inline/OWNERS
new file mode 100644
index 000000000000..edfb2112198a
--- /dev/null
+++ b/core/java/com/android/internal/view/inline/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index 6cc5a4aacda5..83345dad8ed9 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -100,7 +100,6 @@ public class CallLayout extends FrameLayout {
}
// TODO(b/179178086): crop/clip the icon to a circle?
mConversationIconView.setImageIcon(icon);
- mConversationText.setText(callerName);
}
@RemotableViewMethod
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index e143498293f3..2695b9c7651f 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -36,8 +36,10 @@ import android.widget.TextView;
@RemoteViews.RemoteView
public class ImageFloatingTextView extends TextView {
- /** Number of lines from the top to indent */
- private int mIndentLines;
+ /** Number of lines from the top to indent. */
+ private int mIndentLines = 0;
+ /** Whether or not there is an image to indent for. */
+ private boolean mHasImage = false;
/** Resolved layout direction */
private int mResolvedDirection = LAYOUT_DIRECTION_UNDEFINED;
@@ -96,7 +98,7 @@ public class ImageFloatingTextView extends TextView {
// we set the endmargin on the requested number of lines.
int[] margins = null;
- if (mIndentLines > 0) {
+ if (mHasImage && mIndentLines > 0) {
margins = new int[mIndentLines + 1];
for (int i = 0; i < mIndentLines; i++) {
margins[i] = mImageEndMargin;
@@ -111,9 +113,24 @@ public class ImageFloatingTextView extends TextView {
return builder.build();
}
+ /**
+ * @param imageEndMargin the end margin (in pixels) to indent the first few lines of the text
+ */
@RemotableViewMethod
public void setImageEndMargin(int imageEndMargin) {
- mImageEndMargin = imageEndMargin;
+ if (mImageEndMargin != imageEndMargin) {
+ mImageEndMargin = imageEndMargin;
+ invalidateTextIfIndenting();
+ }
+ }
+
+ /**
+ * @param imageEndMarginDp the end margin (in dp) to indent the first few lines of the text
+ */
+ @RemotableViewMethod
+ public void setImageEndMarginDp(float imageEndMarginDp) {
+ setImageEndMargin(
+ (int) (imageEndMarginDp * getResources().getDisplayMetrics().density));
}
@Override
@@ -121,7 +138,7 @@ public class ImageFloatingTextView extends TextView {
int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mPaddingTop - mPaddingBottom;
if (getLayout() != null && getLayout().getHeight() != availableHeight) {
// We've been measured before and the new size is different than before, lets make sure
- // we reset the maximum lines, otherwise we may be cut short
+ // we reset the maximum lines, otherwise the last line of text may be partially cut off
mMaxLinesForHeight = -1;
nullLayouts();
}
@@ -130,7 +147,7 @@ public class ImageFloatingTextView extends TextView {
if (layout.getHeight() > availableHeight) {
// With the existing layout, not all of our lines fit on the screen, let's find the
// first one that fits and ellipsize at that one.
- int maxLines = layout.getLineCount() - 1;
+ int maxLines = layout.getLineCount();
while (maxLines > 1 && layout.getLineBottom(maxLines - 1) > availableHeight) {
maxLines--;
}
@@ -152,31 +169,43 @@ public class ImageFloatingTextView extends TextView {
if (layoutDirection != mResolvedDirection && isLayoutDirectionResolved()) {
mResolvedDirection = layoutDirection;
- if (mIndentLines > 0) {
- // Invalidate layout.
- nullLayouts();
- requestLayout();
- }
+ invalidateTextIfIndenting();
+ }
+ }
+
+ private void invalidateTextIfIndenting() {
+ if (mHasImage && mIndentLines > 0) {
+ // Invalidate layout.
+ nullLayouts();
+ requestLayout();
}
}
+ /**
+ * @param hasImage whether there is an image to wrap text around.
+ */
@RemotableViewMethod
public void setHasImage(boolean hasImage) {
- setNumIndentLines(hasImage ? 2 : 0);
+ setHasImageAndNumIndentLines(hasImage, mIndentLines);
}
/**
* @param lines the number of lines at the top that should be indented by indentEnd
- * @return whether a change was made
*/
- public boolean setNumIndentLines(int lines) {
- if (mIndentLines != lines) {
- mIndentLines = lines;
- // Invalidate layout.
+ @RemotableViewMethod
+ public void setNumIndentLines(int lines) {
+ setHasImageAndNumIndentLines(mHasImage, lines);
+ }
+
+ private void setHasImageAndNumIndentLines(boolean hasImage, int lines) {
+ int oldEffectiveLines = mHasImage ? mIndentLines : 0;
+ int newEffectiveLines = hasImage ? lines : 0;
+ mIndentLines = lines;
+ mHasImage = hasImage;
+ if (oldEffectiveLines != newEffectiveLines) {
+ // always invalidate layout.
nullLayouts();
requestLayout();
- return true;
}
- return false;
}
}
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index d284d5167843..8e68be0f742a 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -1,4 +1,6 @@
per-file PointerLocationView.java = michaelwr@google.com, svv@google.com
+per-file RecyclerView.java = mount@google.com
+per-file ViewPager.java = mount@google.com
# LockSettings related
per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 89a90e944bdf..17ce75e63b6f 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -16,10 +16,16 @@
package com.android.internal.widget;
+import static android.widget.EdgeEffect.TYPE_GLOW;
+import static android.widget.EdgeEffect.TYPE_STRETCH;
+import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_BY_DEFAULT;
+import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED;
+
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
@@ -460,6 +466,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
private final int[] mScrollConsumed = new int[2];
private final int[] mNestedOffsets = new int[2];
+ private int mEdgeEffectType;
+
/**
* These are views that had their a11y importance changed during a layout. We defer these events
* until the end of the layout because a11y service may make sync calls back to the RV while
@@ -587,6 +595,14 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
}
+ boolean defaultToStretch = Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
+ || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.EdgeEffect);
+ mEdgeEffectType = a.getInt(com.android.internal.R.styleable.EdgeEffect_edgeEffectType,
+ defaultToStretch ? TYPE_STRETCH : TYPE_GLOW);
+ a.recycle();
+
// Re-set whether nested scrolling is enabled so that it is set on all API levels
setNestedScrollingEnabled(nestedScrollingEnabled);
}
@@ -610,6 +626,28 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
}
/**
+ * Returns the {@link EdgeEffect#getType()} used for all EdgeEffects.
+ *
+ * @return @link EdgeEffect#getType()} used for all EdgeEffects.
+ */
+ @EdgeEffect.EdgeEffectType
+ public int getEdgeEffectType() {
+ return mEdgeEffectType;
+ }
+
+ /**
+ * Sets the {@link EdgeEffect#getType()} used in all EdgeEffects.
+ * Any existing over-scroll effects are cleared and new effects are created as needed.
+ *
+ * @param type the {@link EdgeEffect#getType()} used in all EdgeEffects.
+ */
+ public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+ mEdgeEffectType = type;
+ invalidateGlows();
+ invalidate();
+ }
+
+ /**
* Instantiate and set a LayoutManager, if specified in the attributes.
*/
private void createLayoutManager(Context context, String className, AttributeSet attrs,
@@ -2183,6 +2221,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
return;
}
mLeftGlow = new EdgeEffect(getContext());
+ mLeftGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2196,6 +2235,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
return;
}
mRightGlow = new EdgeEffect(getContext());
+ mRightGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2209,6 +2249,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
return;
}
mTopGlow = new EdgeEffect(getContext());
+ mTopGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2223,6 +2264,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
return;
}
mBottomGlow = new EdgeEffect(getContext());
+ mBottomGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2663,7 +2705,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
- if (mScrollState == SCROLL_STATE_SETTLING) {
+ if (stopGlowAnimations(e) || mScrollState == SCROLL_STATE_SETTLING) {
getParent().requestDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
}
@@ -2731,6 +2773,38 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
return mScrollState == SCROLL_STATE_DRAGGING;
}
+ /**
+ * This stops any edge glow animation that is currently running by applying a
+ * 0 length pull at the displacement given by the provided MotionEvent. On pre-S devices,
+ * this method does nothing, allowing any animating edge effect to continue animating and
+ * returning <code>false</code> always.
+ *
+ * @param e The motion event to use to indicate the finger position for the displacement of
+ * the current pull.
+ * @return <code>true</code> if any edge effect had an existing effect to be drawn ond the
+ * animation was stopped or <code>false</code> if no edge effect had a value to display.
+ */
+ private boolean stopGlowAnimations(MotionEvent e) {
+ boolean stopped = false;
+ if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
+ mLeftGlow.onPullDistance(0, 1 - (e.getY() / getHeight()));
+ stopped = true;
+ }
+ if (mRightGlow != null && mRightGlow.getDistance() != 0) {
+ mRightGlow.onPullDistance(0, e.getY() / getHeight());
+ stopped = true;
+ }
+ if (mTopGlow != null && mTopGlow.getDistance() != 0) {
+ mTopGlow.onPullDistance(0, e.getX() / getWidth());
+ stopped = true;
+ }
+ if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
+ mBottomGlow.onPullDistance(0, 1 - e.getX() / getWidth());
+ stopped = true;
+ }
+ return stopped;
+ }
+
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
final int listenerCount = mOnItemTouchListeners.size();
@@ -2807,6 +2881,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
final int y = (int) (e.getY(index) + 0.5f);
int dx = mLastTouchX - x;
int dy = mLastTouchY - y;
+ dx -= releaseHorizontalGlow(dx, e.getY());
+ dy -= releaseVerticalGlow(dy, e.getX());
if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
dx -= mScrollConsumed[0];
@@ -2887,6 +2963,72 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
return true;
}
+ /**
+ * If either of the horizontal edge glows are currently active, this consumes part or all of
+ * deltaX on the edge glow.
+ *
+ * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive
+ * for moving down and negative for moving up.
+ * @param y The vertical position of the pointer.
+ * @return The amount of <code>deltaX</code> that has been consumed by the
+ * edge glow.
+ */
+ private int releaseHorizontalGlow(int deltaX, float y) {
+ // First allow releasing existing overscroll effect:
+ float consumed = 0;
+ float displacement = y / getHeight();
+ float pullDistance = (float) deltaX / getWidth();
+ if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
+ consumed = -mLeftGlow.onPullDistance(-pullDistance, 1 - displacement);
+ if (mLeftGlow.getDistance() == 0) {
+ mLeftGlow.onRelease();
+ }
+ } else if (mRightGlow != null && mRightGlow.getDistance() != 0) {
+ consumed = mRightGlow.onPullDistance(pullDistance, displacement);
+ if (mRightGlow.getDistance() == 0) {
+ mRightGlow.onRelease();
+ }
+ }
+ int pixelsConsumed = Math.round(consumed * getWidth());
+ if (pixelsConsumed != 0) {
+ invalidate();
+ }
+ return pixelsConsumed;
+ }
+
+ /**
+ * If either of the vertical edge glows are currently active, this consumes part or all of
+ * deltaY on the edge glow.
+ *
+ * @param deltaY The pointer motion, in pixels, in the vertical direction, positive
+ * for moving down and negative for moving up.
+ * @param x The vertical position of the pointer.
+ * @return The amount of <code>deltaY</code> that has been consumed by the
+ * edge glow.
+ */
+ private int releaseVerticalGlow(int deltaY, float x) {
+ // First allow releasing existing overscroll effect:
+ float consumed = 0;
+ float displacement = x / getWidth();
+ float pullDistance = (float) deltaY / getHeight();
+ if (mTopGlow != null && mTopGlow.getDistance() != 0) {
+ consumed = -mTopGlow.onPullDistance(-pullDistance, displacement);
+ if (mTopGlow.getDistance() == 0) {
+ mTopGlow.onRelease();
+ }
+ } else if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
+ consumed = mBottomGlow.onPullDistance(pullDistance, 1 - displacement);
+ if (mBottomGlow.getDistance() == 0) {
+ mBottomGlow.onRelease();
+ }
+ }
+ int pixelsConsumed = Math.round(consumed * getHeight());
+ if (pixelsConsumed != 0) {
+ invalidate();
+ }
+ return pixelsConsumed;
+ }
+
private void resetTouch() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d6d33873adaa..a153fab10214 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -1,4 +1,3 @@
-
package {
default_applicable_licenses: ["frameworks_base_core_jni_license"],
}
@@ -69,10 +68,9 @@ cc_library_shared {
"liblog",
"libminikin",
"libz",
- "libziparchive",
],
- static_libs: ["libnativehelper_lazy"],
+ static_libs: ["libnativehelper_lazy", "libziparchive_for_incfs", ],
export_include_dirs: [
".",
@@ -214,10 +212,12 @@ cc_library_shared {
"com_android_internal_os_Zygote.cpp",
"com_android_internal_os_ZygoteCommandBuffer.cpp",
"com_android_internal_os_ZygoteInit.cpp",
+ "com_android_internal_security_VerityUtils.cpp",
"hwbinder/EphemeralStorage.cpp",
"fd_utils.cpp",
"android_hardware_input_InputWindowHandle.cpp",
"android_hardware_input_InputApplicationHandle.cpp",
+ "permission_utils.cpp",
],
static_libs: [
@@ -236,6 +236,7 @@ cc_library_shared {
"audioflinger-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
+ "media_permission-aidl-cpp",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ddd861380fab..94ac183517a2 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -97,6 +97,7 @@ extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);
extern int register_android_media_midi(JNIEnv *env);
+extern int register_android_media_permission_Identity(JNIEnv* env);
namespace android {
@@ -200,6 +201,7 @@ extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *en
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
+extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
// Namespace for Android Runtime flags applied during boot time.
@@ -635,6 +637,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+ char madviseWillNeedFileSizeVdex[
+ sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+ char madviseWillNeedFileSizeOdex[
+ sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+ char madviseWillNeedFileSizeArt[
+ sizeof("-XMadviseWillNeedArtFileSize:")-1 + PROPERTY_VALUE_MAX];
char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -843,6 +851,22 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
/*
+ * Use default platform configuration as limits for madvising,
+ * when no properties are specified.
+ */
+ parseRuntimeOption("dalvik.vm.madvise.vdexfile.size",
+ madviseWillNeedFileSizeVdex,
+ "-XMadviseWillNeedVdexFileSize:");
+
+ parseRuntimeOption("dalvik.vm.madvise.odexfile.size",
+ madviseWillNeedFileSizeOdex,
+ "-XMadviseWillNeedOdexFileSize:");
+
+ parseRuntimeOption("dalvik.vm.madvise.artfile.size",
+ madviseWillNeedFileSizeArt,
+ "-XMadviseWillNeedArtFileSize:");
+
+ /*
* Profile related options.
*/
parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
@@ -1534,6 +1558,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
REG_JNI(register_com_android_internal_os_ZygoteInit),
+ REG_JNI(register_com_android_internal_security_VerityUtils),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
@@ -1562,6 +1587,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_RemoteDisplay),
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_media_midi),
+ REG_JNI(register_android_media_permission_Identity),
REG_JNI(register_android_opengl_classes),
REG_JNI(register_android_server_NetworkManagementSocketTagger),
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 0e0f98ec1fc4..7d3febbd5d14 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -22,6 +22,7 @@
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
+#include "permission_utils.h"
#include <utils/Log.h>
#include <media/AudioRecord.h>
@@ -39,6 +40,9 @@
// ----------------------------------------------------------------------------
+using android::media::permission::convertIdentity;
+using android::media::permission::Identity;
+
using namespace android;
// ----------------------------------------------------------------------------
@@ -181,12 +185,11 @@ static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioR
}
// ----------------------------------------------------------------------------
-static jint
-android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
- jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
- jlong nativeRecordInJavaObj)
-{
+static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+ jobject jaa, jintArray jSampleRate, jint channelMask,
+ jint channelIndexMask, jint audioFormat,
+ jint buffSizeInBytes, jintArray jSession,
+ jobject jIdentity, jlong nativeRecordInJavaObj) {
//ALOGV(">> Entering android_media_AudioRecord_setup");
//ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
// "nativeRecordInJavaObj=0x%llX",
@@ -262,10 +265,8 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
size_t frameSize = channelCount * bytesPerSample;
size_t frameCount = buffSizeInBytes / frameSize;
- ScopedUtfChars opPackageNameStr(env, opPackageName);
-
// create an uninitialized AudioRecord object
- lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
+ lpRecorder = new AudioRecord(convertIdentity(env, jIdentity));
// read the AudioAttributes values
auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -373,8 +374,6 @@ native_init_failure:
return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
}
-
-
// ----------------------------------------------------------------------------
static jint
android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
@@ -893,9 +892,11 @@ static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
+ // name, signature, funcPtr
{"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
+ {"native_setup",
+ "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/media/permission/Identity;J)I",
(void *)android_media_AudioRecord_setup},
{"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
{"native_release", "()V", (void *)android_media_AudioRecord_release},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 94bd28a59e7c..f102edc79e7f 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2204,13 +2204,11 @@ android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP(
return jStatus;
}
-static jint
-android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
- jobject jSurroundFormats, jboolean reported)
-{
+static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
+ jobject jSurroundFormats) {
ALOGV("getSurroundFormats");
- if (jSurroundFormats == NULL) {
+ if (jSurroundFormats == nullptr) {
ALOGE("jSurroundFormats is NULL");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
@@ -2221,10 +2219,10 @@ android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
jint jStatus;
unsigned int numSurroundFormats = 0;
- audio_format_t *surroundFormats = NULL;
- bool *surroundFormatsEnabled = NULL;
- status_t status = AudioSystem::getSurroundFormats(
- &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+ audio_format_t *surroundFormats = nullptr;
+ bool *surroundFormatsEnabled = nullptr;
+ status_t status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats,
+ surroundFormatsEnabled);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
jStatus = nativeToJavaStatus(status);
@@ -2236,8 +2234,8 @@ android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
}
surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool));
- status = AudioSystem::getSurroundFormats(
- &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+ status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats,
+ surroundFormatsEnabled);
jStatus = nativeToJavaStatus(status);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
@@ -2258,6 +2256,50 @@ exit:
return jStatus;
}
+static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jobject thiz,
+ jobject jSurroundFormats) {
+ ALOGV("getReportedSurroundFormats");
+
+ if (jSurroundFormats == nullptr) {
+ ALOGE("jSurroundFormats is NULL");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jSurroundFormats, gArrayListClass)) {
+ ALOGE("jSurroundFormats not an arraylist");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ jint jStatus;
+ unsigned int numSurroundFormats = 0;
+ audio_format_t *surroundFormats = nullptr;
+ status_t status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status);
+ jStatus = nativeToJavaStatus(status);
+ goto exit;
+ }
+ if (numSurroundFormats == 0) {
+ jStatus = (jint)AUDIO_JAVA_SUCCESS;
+ goto exit;
+ }
+ surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
+ status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+ jStatus = nativeToJavaStatus(status);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status);
+ goto exit;
+ }
+ for (size_t i = 0; i < numSurroundFormats; i++) {
+ jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor,
+ audioFormatFromNative(surroundFormats[i]));
+ env->CallObjectMethod(jSurroundFormats, gArrayListMethods.add, surroundFormat);
+ env->DeleteLocalRef(surroundFormat);
+ }
+
+exit:
+ free(surroundFormats);
+ return jStatus;
+}
+
static jint
android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz,
jint audioFormat, jboolean enabled)
@@ -2619,8 +2661,10 @@ static const JNINativeMethod gMethods[] =
(void *)android_media_AudioSystem_getOffloadSupport},
{"getMicrophones", "(Ljava/util/ArrayList;)I",
(void *)android_media_AudioSystem_getMicrophones},
- {"getSurroundFormats", "(Ljava/util/Map;Z)I",
+ {"getSurroundFormats", "(Ljava/util/Map;)I",
(void *)android_media_AudioSystem_getSurroundFormats},
+ {"getReportedSurroundFormats", "(Ljava/util/ArrayList;)I",
+ (void *)android_media_AudioSystem_getReportedSurroundFormats},
{"setSurroundFormatEnabled", "(IZ)I",
(void *)android_media_AudioSystem_setSurroundFormatEnabled},
{"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index cae6db57e99c..62767a676e8b 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -48,6 +48,7 @@
using namespace android;
using ::android::media::VolumeShaper;
+using ::android::media::permission::Identity;
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/AudioTrack";
@@ -328,7 +329,10 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
// create the native AudioTrack object
ScopedUtfChars opPackageNameStr(env, opPackageName);
- lpTrack = new AudioTrack(opPackageNameStr.c_str());
+ // TODO b/182469354: make consistent with AudioRecord
+ Identity identity = Identity();
+ identity.packageName = std::string(opPackageNameStr.c_str());
+ lpTrack = new AudioTrack(identity);
// read the AudioAttributes values
auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -390,8 +394,8 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
sessionId, // audio session ID
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
: AudioTrack::TRANSFER_SYNC,
- (offload || encapsulationMode) ? &offloadInfo : NULL, -1,
- -1, // default uid, pid values
+ (offload || encapsulationMode) ? &offloadInfo : NULL,
+ Identity(), // default uid, pid values
paa.get());
break;
@@ -416,8 +420,8 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
true, // thread can call Java
sessionId, // audio session ID
AudioTrack::TRANSFER_SHARED,
- NULL, // default offloadInfo
- -1, -1, // default uid, pid values
+ NULL, // default offloadInfo
+ Identity(), // default uid, pid values
paa.get());
break;
diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp
index 750810840bde..a781a377694b 100644
--- a/core/jni/android_net_NetworkUtils.cpp
+++ b/core/jni/android_net_NetworkUtils.cpp
@@ -52,27 +52,6 @@ constexpr int MAXPACKETSIZE = 8 * 1024;
// FrameworkListener limits the size of commands to 4096 bytes.
constexpr int MAXCMDSIZE = 4096;
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
- static jclass errnoExceptionClass =
- MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
- static jmethodID errnoExceptionCtor =
- GetMethodIDOrDie(env, errnoExceptionClass,
- "<init>", "(Ljava/lang/String;I)V");
-
- jobject exception = env->NewObject(errnoExceptionClass,
- errnoExceptionCtor,
- detailMessage.get(),
- error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
struct sock_filter filter_code[] = {
@@ -150,7 +129,7 @@ static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint
int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
if (fd < 0) {
- throwErrnoException(env, "resNetworkQuery", -fd);
+ jniThrowErrnoException(env, "resNetworkQuery", -fd);
return nullptr;
}
@@ -165,7 +144,7 @@ static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint
int fd = resNetworkSend(netId, data, msgLen, flags);
if (fd < 0) {
- throwErrnoException(env, "resNetworkSend", -fd);
+ jniThrowErrnoException(env, "resNetworkSend", -fd);
return nullptr;
}
@@ -180,13 +159,13 @@ static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, job
int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
- throwErrnoException(env, "resNetworkResult", -res);
+ jniThrowErrnoException(env, "resNetworkResult", -res);
return nullptr;
}
jbyteArray answer = env->NewByteArray(res);
if (answer == nullptr) {
- throwErrnoException(env, "resNetworkResult", ENOMEM);
+ jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
return nullptr;
} else {
env->SetByteArrayRegion(answer, 0, res,
@@ -208,7 +187,7 @@ static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobjec
static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
unsigned dnsNetId = 0;
if (int res = getNetworkForDns(&dnsNetId) < 0) {
- throwErrnoException(env, "getDnsNetId", -res);
+ jniThrowErrnoException(env, "getDnsNetId", -res);
return nullptr;
}
bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
@@ -233,8 +212,8 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j
// Obtain the parameters of the TCP repair window.
int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
if (rc == -1) {
- throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
- return NULL;
+ jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+ return NULL;
}
struct tcp_info tcpinfo = {};
@@ -244,8 +223,8 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j
// should be applied to the window size.
rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
if (rc == -1) {
- throwErrnoException(env, "getsockopt : TCP_INFO", errno);
- return NULL;
+ jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
+ return NULL;
}
jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index dc86187d8fea..fe375f4fa359 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -35,21 +35,6 @@ namespace {
jclass errnoExceptionClass;
jmethodID errnoExceptionCtor; // MethodID for ErrnoException.<init>(String,I)
-void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
-
- jobject exception = env->NewObject(errnoExceptionClass,
- errnoExceptionCtor,
- detailMessage.get(),
- error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
// Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
@@ -65,7 +50,7 @@ jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
}
if (fd < 0) {
- throwErrnoException(env, "SharedMemory_create", err);
+ jniThrowErrnoException(env, "SharedMemory_create", err);
return nullptr;
}
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 2384efaf1a54..413bcef57a64 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -41,6 +41,10 @@ static jboolean nativeIsIncrementalPath(JNIEnv* env,
return (jboolean)IncFs_IsIncFsPath(path.c_str());
}
+static jboolean nativeIsIncrementalFd(JNIEnv* env, jobject clazz, jint fd) {
+ return (jboolean)IncFs_IsIncFsFd(fd);
+}
+
static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) {
ScopedUtfChars path(env, javaPath);
@@ -61,6 +65,7 @@ static const JNINativeMethod method_table[] =
{{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
{"nativeIsV2Available", "()Z", (void*)nativeIsV2Available},
{"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
+ {"nativeIsIncrementalFd", "(I)Z", (void*)nativeIsIncrementalFd},
{"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B",
(void*)nativeUnsafeGetFileSignature}};
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f7b3f309ed12..2e4be145d54d 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1509,7 +1509,9 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
res = JNI_TRUE;
} else {
jniThrowException(env, "java/util/NoSuchElementException",
- "Death link does not exist");
+ base::StringPrintf("Death link does not exist (%s)",
+ statusToString(err).c_str())
+ .c_str());
}
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index dcfa95054ada..d4b5c2bde31a 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1316,24 +1316,6 @@ void android_os_Process_removeAllProcessGroups(JNIEnv* env, jobject clazz)
return removeAllProcessGroups();
}
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
- static jclass errnoExceptionClass =
- MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
- static jmethodID errnoExceptionCtor =
- GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V");
-
- jobject exception =
- env->NewObject(errnoExceptionClass, errnoExceptionCtor, detailMessage.get(), error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
// Wrapper function to the syscall pidfd_open, which creates a file
// descriptor that refers to the process whose PID is specified in pid.
static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
@@ -1343,7 +1325,7 @@ static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
int fd = sys_pidfd_open(pid, flags);
if (fd < 0) {
- throwErrnoException(env, "nativePidFdOpen", errno);
+ jniThrowErrnoException(env, "nativePidFdOpen", errno);
return -1;
}
return fd;
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 5e142fd10de0..bfeb01d22bdf 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -45,18 +45,13 @@ static const char* toString(bool value) {
return value ? "true" : "false";
}
-enum class HandleEventResponse : int {
- // Allowed return values of 'handleEvent' function as documented in LooperCallback::handleEvent
- REMOVE_CALLBACK = 0,
- KEEP_CALLBACK = 1
-};
-
static struct {
jclass clazz;
jmethodID dispatchInputEvent;
jmethodID onFocusEvent;
jmethodID onPointerCaptureEvent;
+ jmethodID onDragEvent;
jmethodID onBatchedInputEventPending;
} gInputEventReceiverClassInfo;
@@ -76,14 +71,6 @@ static std::string addPrefix(std::string str, std::string_view prefix) {
return str;
}
-/**
- * Convert an enumeration to its underlying type. Replace with std::to_underlying when available.
- */
-template <class T>
-static std::underlying_type_t<T> toUnderlying(const T& t) {
- return static_cast<std::underlying_type_t<T>>(t);
-}
-
class NativeInputEventReceiver : public LooperCallback {
public:
NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -120,16 +107,11 @@ private:
return mInputConsumer.getChannel()->getName();
}
- HandleEventResponse processOutboundEvents();
+ status_t processOutboundEvents();
// From 'LooperCallback'
int handleEvent(int receiveFd, int events, void* data) override;
};
-// Ensure HandleEventResponse underlying type matches the return type of LooperCallback::handleEvent
-static_assert(std::is_same<std::underlying_type_t<HandleEventResponse>,
- std::invoke_result_t<decltype(&LooperCallback::handleEvent),
- NativeInputEventReceiver, int, int, void*>>::value);
-
NativeInputEventReceiver::NativeInputEventReceiver(
JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue)
@@ -166,26 +148,12 @@ status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled)
ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
}
- status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
- if (status != OK) {
- if (status == WOULD_BLOCK) {
- if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Could not send finished signal immediately. "
- "Enqueued for later.", getInputChannelName().c_str());
- }
- Finish finish;
- finish.seq = seq;
- finish.handled = handled;
- mFinishQueue.push_back(finish);
- if (mFinishQueue.size() == 1) {
- setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
- }
- return OK;
- }
- ALOGW("Failed to send finished signal on channel '%s'. status=%d",
- getInputChannelName().c_str(), status);
- }
- return status;
+ Finish finish{
+ .seq = seq,
+ .handled = handled,
+ };
+ mFinishQueue.push_back(finish);
+ return processOutboundEvents();
}
void NativeInputEventReceiver::setFdEvents(int events) {
@@ -216,7 +184,7 @@ void NativeInputEventReceiver::setFdEvents(int events) {
* InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up
* unnecessarily.
*/
-HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
+status_t NativeInputEventReceiver::processOutboundEvents() {
while (!mFinishQueue.empty()) {
const Finish& finish = *mFinishQueue.begin();
status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
@@ -232,7 +200,8 @@ HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
ALOGD("channel '%s' ~ Remaining outbound events: %zu.",
getInputChannelName().c_str(), mFinishQueue.size());
}
- return HandleEventResponse::KEEP_CALLBACK; // try again later
+ setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
+ return WOULD_BLOCK; // try again later
}
// Some other error. Give up
@@ -246,42 +215,49 @@ HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
jniThrowRuntimeException(env, message.c_str());
mMessageQueue->raiseAndClearException(env, "finishInputEvent");
}
- return HandleEventResponse::REMOVE_CALLBACK;
+ return status;
}
// The queue is now empty. Tell looper there's no more output to expect.
setFdEvents(ALOOPER_EVENT_INPUT);
- return HandleEventResponse::KEEP_CALLBACK;
+ return OK;
}
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
+ // Allowed return values of this function as documented in LooperCallback::handleEvent
+ constexpr int REMOVE_CALLBACK = 0;
+ constexpr int KEEP_CALLBACK = 1;
+
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
// This error typically occurs when the publisher has closed the input channel
// as part of removing a window or finishing an IME session, in which case
// the consumer will soon be disposed as well.
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. "
- "events=0x%x", getInputChannelName().c_str(), events);
+ ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. events=0x%x",
+ getInputChannelName().c_str(), events);
}
- return toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
+ return REMOVE_CALLBACK;
}
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
- return status == OK || status == NO_MEMORY
- ? toUnderlying(HandleEventResponse::KEEP_CALLBACK)
- : toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
+ return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
}
if (events & ALOOPER_EVENT_OUTPUT) {
- return toUnderlying(processOutboundEvents());
+ const status_t status = processOutboundEvents();
+ if (status == OK || status == WOULD_BLOCK) {
+ return KEEP_CALLBACK;
+ } else {
+ return REMOVE_CALLBACK;
+ }
}
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", getInputChannelName().c_str(), events);
- return toUnderlying(HandleEventResponse::KEEP_CALLBACK);
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x",
+ getInputChannelName().c_str(), events);
+ return KEEP_CALLBACK;
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
@@ -400,6 +376,18 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
finishInputEvent(seq, true /* handled */);
continue;
}
+ case AINPUT_EVENT_TYPE_DRAG: {
+ const DragEvent* dragEvent = static_cast<DragEvent*>(inputEvent);
+ if (kDebugDispatchCycle) {
+ ALOGD("channel '%s' ~ Received drag event: isExiting=%s",
+ getInputChannelName().c_str(), toString(dragEvent->isExiting()));
+ }
+ env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onDragEvent,
+ jboolean(dragEvent->isExiting()), dragEvent->getX(),
+ dragEvent->getY());
+ finishInputEvent(seq, true /* handled */);
+ continue;
+ }
default:
assert(false); // InputConsumer should prevent this from ever happening
@@ -490,9 +478,13 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
sp<NativeInputEventReceiver> receiver =
reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
status_t status = receiver->finishInputEvent(seq, handled);
- if (status && status != DEAD_OBJECT) {
+ if (status == OK || status == WOULD_BLOCK) {
+ return; // normal operation
+ }
+ if (status != DEAD_OBJECT) {
std::string message =
- android::base::StringPrintf("Failed to finish input event. status=%d", status);
+ android::base::StringPrintf("Failed to finish input event. status=%s(%d)",
+ strerror(-status), status);
jniThrowRuntimeException(env, message.c_str());
}
}
@@ -547,6 +539,8 @@ int register_android_view_InputEventReceiver(JNIEnv* env) {
gInputEventReceiverClassInfo.onPointerCaptureEvent =
GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent",
"(Z)V");
+ gInputEventReceiverClassInfo.onDragEvent =
+ GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onDragEvent", "(ZFF)V");
gInputEventReceiverClassInfo.onBatchedInputEventPending =
GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending",
"(I)V");
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 9746a07a1b77..52d21a858d4f 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -34,6 +34,8 @@
#include "core_jni_helpers.h"
+using android::base::Result;
+
namespace android {
// Log debug messages about the dispatch cycle.
@@ -170,16 +172,16 @@ int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) {
// as part of finishing an IME session, in which case the publisher will
// soon be disposed as well.
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. "
- "events=0x%x", getInputChannelName().c_str(), events);
+ ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
+ getInputChannelName().c_str(), events);
}
return 0; // remove the callback
}
if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", getInputChannelName().c_str(), events);
+ ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x",
+ getInputChannelName().c_str(), events);
return 1;
}
@@ -197,16 +199,9 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
ScopedLocalRef<jobject> senderObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
- uint32_t publishedSeq;
- bool handled;
- std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
- [&publishedSeq, &handled](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- publishedSeq = inSeq;
- handled = inHandled;
- };
- status_t status = mInputPublisher.receiveFinishedSignal(callback);
- if (status) {
+ Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal();
+ if (!result.ok()) {
+ const status_t status = result.error().code();
if (status == WOULD_BLOCK) {
return OK;
}
@@ -215,7 +210,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
return status;
}
- auto it = mPublishedSeqMap.find(publishedSeq);
+ auto it = mPublishedSeqMap.find(result->seq);
if (it == mPublishedSeqMap.end()) {
continue;
}
@@ -224,25 +219,24 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
mPublishedSeqMap.erase(it);
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
- "pendingEvents=%zu.",
- getInputChannelName().c_str(), seq, handled ? "true" : "false",
- mPublishedSeqMap.size());
+ ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
+ getInputChannelName().c_str(), seq, result->handled ? "true" : "false",
+ mPublishedSeqMap.size());
}
if (!skipCallbacks) {
if (!senderObj.get()) {
senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
if (!senderObj.get()) {
- ALOGW("channel '%s' ~ Sender object was finalized "
- "without being disposed.", getInputChannelName().c_str());
+ ALOGW("channel '%s' ~ Sender object was finalized without being disposed.",
+ getInputChannelName().c_str());
return DEAD_OBJECT;
}
}
env->CallVoidMethod(senderObj.get(),
- gInputEventSenderClassInfo.dispatchInputEventFinished,
- jint(seq), jboolean(handled));
+ gInputEventSenderClassInfo.dispatchInputEventFinished,
+ static_cast<jint>(seq), static_cast<jboolean>(result->handled));
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching finished signal.");
skipCallbacks = true;
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ebc507a8381e..469e577829d8 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -16,9 +16,10 @@
#include <android_runtime/AndroidRuntime.h>
-#include <input/KeyCharacterMap.h>
-#include <input/Input.h>
#include <binder/Parcel.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/KeyCharacterMap.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
@@ -75,6 +76,10 @@ jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
reinterpret_cast<jlong>(nativeMap));
}
+static jobject nativeObtainEmptyKeyCharacterMap(JNIEnv* env, jobject /* clazz */, jint deviceId) {
+ return android_view_KeyCharacterMap_create(env, deviceId, nullptr);
+}
+
static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (!parcel) {
@@ -224,33 +229,37 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
return result;
}
+static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) {
+ const std::shared_ptr<KeyCharacterMap>& map1 =
+ (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap();
+ const std::shared_ptr<KeyCharacterMap>& map2 =
+ (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap();
+ if (map1 == nullptr || map2 == nullptr) {
+ return map1 == map2;
+ }
+ return static_cast<jboolean>(*map1 == *map2);
+}
/*
* JNI registration.
*/
static const JNINativeMethod g_methods[] = {
- /* name, signature, funcPtr */
- { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
- (void*)nativeReadFromParcel },
- { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
- (void*)nativeWriteToParcel },
- { "nativeDispose", "(J)V",
- (void*)nativeDispose },
- { "nativeGetCharacter", "(JII)C",
- (void*)nativeGetCharacter },
- { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
- (void*)nativeGetFallbackAction },
- { "nativeGetNumber", "(JI)C",
- (void*)nativeGetNumber },
- { "nativeGetMatch", "(JI[CI)C",
- (void*)nativeGetMatch },
- { "nativeGetDisplayLabel", "(JI)C",
- (void*)nativeGetDisplayLabel },
- { "nativeGetKeyboardType", "(J)I",
- (void*)nativeGetKeyboardType },
- { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;",
- (void*)nativeGetEvents },
+ /* name, signature, funcPtr */
+ {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel},
+ {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
+ {"nativeDispose", "(J)V", (void*)nativeDispose},
+ {"nativeGetCharacter", "(JII)C", (void*)nativeGetCharacter},
+ {"nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
+ (void*)nativeGetFallbackAction},
+ {"nativeGetNumber", "(JI)C", (void*)nativeGetNumber},
+ {"nativeGetMatch", "(JI[CI)C", (void*)nativeGetMatch},
+ {"nativeGetDisplayLabel", "(JI)C", (void*)nativeGetDisplayLabel},
+ {"nativeGetKeyboardType", "(J)I", (void*)nativeGetKeyboardType},
+ {"nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", (void*)nativeGetEvents},
+ {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
+ (void*)nativeObtainEmptyKeyCharacterMap},
+ {"nativeEquals", "(JJ)Z", (void*)nativeEquals},
};
int register_android_view_KeyCharacterMap(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cbf4481bd2f1..65b8b988f38b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -746,7 +746,7 @@ static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jlong transactionObj,
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
Rect crop(l, t, r, b);
- transaction->setCrop_legacy(ctrl, crop);
+ transaction->setCrop(ctrl, crop);
}
static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -1528,11 +1528,17 @@ static jboolean nativeGetDisplayBrightnessSupport(JNIEnv* env, jclass clazz,
}
static jboolean nativeSetDisplayBrightness(JNIEnv* env, jclass clazz, jobject displayTokenObject,
- jfloat brightness) {
+ jfloat sdrBrightness, jfloat sdrBrightnessNits,
+ jfloat displayBrightness, jfloat displayBrightnessNits) {
sp<IBinder> displayToken(ibinderForJavaObject(env, displayTokenObject));
if (displayToken == nullptr) {
return JNI_FALSE;
}
+ gui::DisplayBrightness brightness;
+ brightness.sdrWhitePoint = sdrBrightness;
+ brightness.sdrWhitePointNits = sdrBrightnessNits;
+ brightness.displayBrightness = displayBrightness;
+ brightness.displayBrightnessNits = displayBrightnessNits;
status_t error = SurfaceComposerClient::setDisplayBrightness(displayToken, brightness);
return error == OK ? JNI_TRUE : JNI_FALSE;
}
@@ -1860,7 +1866,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSyncInputWindows },
{"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z",
(void*)nativeGetDisplayBrightnessSupport },
- {"nativeSetDisplayBrightness", "(Landroid/os/IBinder;F)Z",
+ {"nativeSetDisplayBrightness", "(Landroid/os/IBinder;FFFF)Z",
(void*)nativeSetDisplayBrightness },
{"nativeReadTransactionFromParcel", "(Landroid/os/Parcel;)J",
(void*)nativeReadTransactionFromParcel },
diff --git a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
index 6b41b2ec8f93..d644e3709045 100644
--- a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
@@ -30,13 +30,13 @@ static jboolean KernelCpuBpfTracking_startTrackingInternal(JNIEnv *, jobject) {
static jlongArray KernelCpuBpfTracking_getFreqsInternal(JNIEnv *env, jobject) {
auto freqs = android::bpf::getCpuFreqs();
- if (!freqs) return NULL;
+ if (!freqs) return nullptr;
std::vector<uint64_t> allFreqs;
for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
auto ar = env->NewLongArray(allFreqs.size());
- if (ar != NULL) {
+ if (ar != nullptr) {
env->SetLongArrayRegion(ar, 0, allFreqs.size(),
reinterpret_cast<const jlong *>(allFreqs.data()));
}
@@ -45,7 +45,7 @@ static jlongArray KernelCpuBpfTracking_getFreqsInternal(JNIEnv *env, jobject) {
static jintArray KernelCpuBpfTracking_getFreqsClustersInternal(JNIEnv *env, jobject) {
auto freqs = android::bpf::getCpuFreqs();
- if (!freqs) return NULL;
+ if (!freqs) return nullptr;
std::vector<uint32_t> freqsClusters;
uint32_t clusters = freqs->size();
@@ -54,7 +54,7 @@ static jintArray KernelCpuBpfTracking_getFreqsClustersInternal(JNIEnv *env, jobj
}
auto ar = env->NewIntArray(freqsClusters.size());
- if (ar != NULL) {
+ if (ar != nullptr) {
env->SetIntArrayRegion(ar, 0, freqsClusters.size(),
reinterpret_cast<const jint *>(freqsClusters.data()));
}
diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
index ad43014d321f..472bd23c0f8d 100644
--- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
@@ -22,7 +22,7 @@ namespace android {
static jlongArray KernelCpuTotalBpfMapReader_readInternal(JNIEnv *env, jobject) {
auto freqTimes = android::bpf::getTotalCpuFreqTimes();
- if (!freqTimes) return JNI_FALSE;
+ if (!freqTimes) return nullptr;
std::vector<uint64_t> allTimes;
for (const auto &vec : *freqTimes) {
@@ -32,7 +32,7 @@ static jlongArray KernelCpuTotalBpfMapReader_readInternal(JNIEnv *env, jobject)
}
auto ar = env->NewLongArray(allTimes.size());
- if (ar != NULL) {
+ if (ar != nullptr) {
env->SetLongArrayRegion(ar, 0, allTimes.size(),
reinterpret_cast<const jlong *>(allTimes.data()));
}
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
index 7900d301dbb0..098a4d868269 100644
--- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -37,7 +37,7 @@ static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize
jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
if (!ar) {
ar = env->NewLongArray(sz);
- if (ar == NULL) return ar;
+ if (ar == nullptr) return ar;
env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
}
return ar;
@@ -65,7 +65,7 @@ static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobjec
static uint64_t lastUpdate = 0;
uint64_t newLastUpdate = lastUpdate;
auto sparseAr = env->GetObjectField(thiz, gmData);
- if (sparseAr == NULL) return false;
+ if (sparseAr == nullptr) return false;
auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
if (!data.has_value()) return false;
@@ -75,7 +75,7 @@ static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobjec
for (const auto &subVec : times) s += subVec.size();
}
jlongArray ar = getUidArray(env, sparseAr, uid, s);
- if (ar == NULL) return false;
+ if (ar == nullptr) return false;
copy2DVecToArray(env, ar, times);
}
lastUpdate = newLastUpdate;
@@ -91,7 +91,7 @@ static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobj
static uint64_t lastUpdate = 0;
uint64_t newLastUpdate = lastUpdate;
auto sparseAr = env->GetObjectField(thiz, gmData);
- if (sparseAr == NULL) return false;
+ if (sparseAr == nullptr) return false;
auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
if (!data.has_value()) return false;
@@ -99,7 +99,7 @@ static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobj
// TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
for (auto &time : times.active) time /= NSEC_PER_MSEC;
jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
- if (ar == NULL) return false;
+ if (ar == nullptr) return false;
env->SetLongArrayRegion(ar, 0, times.active.size(),
reinterpret_cast<const jlong *>(times.active.data()));
}
@@ -111,7 +111,7 @@ static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *e
jlong nCpus = get_nprocs_conf();
auto ar = env->NewLongArray(1);
- if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
+ if (ar != nullptr) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
return ar;
}
@@ -124,7 +124,7 @@ static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, job
static uint64_t lastUpdate = 0;
uint64_t newLastUpdate = lastUpdate;
auto sparseAr = env->GetObjectField(thiz, gmData);
- if (sparseAr == NULL) return false;
+ if (sparseAr == nullptr) return false;
auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
if (!data.has_value()) return false;
@@ -134,7 +134,7 @@ static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, job
for (const auto &subVec : times.policy) s += subVec.size();
}
jlongArray ar = getUidArray(env, sparseAr, uid, s);
- if (ar == NULL) return false;
+ if (ar == nullptr) return false;
copy2DVecToArray(env, ar, times.policy);
}
lastUpdate = newLastUpdate;
@@ -143,12 +143,12 @@ static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, job
static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
auto times = android::bpf::getUidConcurrentTimes(0);
- if (!times.has_value()) return NULL;
+ if (!times.has_value()) return nullptr;
std::vector<jlong> clusterCores;
for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
auto ar = env->NewLongArray(clusterCores.size());
- if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
+ if (ar != nullptr) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
return ar;
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 836074f1d5f7..be17d92b78ba 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -184,6 +184,17 @@ static constexpr int PROCESS_PRIORITY_MIN = 19;
/** The numeric value for the normal priority a process should have. */
static constexpr int PROCESS_PRIORITY_DEFAULT = 0;
+/** Exponential back off parameters for storage dir check. */
+static constexpr unsigned int STORAGE_DIR_CHECK_RETRY_MULTIPLIER = 2;
+static constexpr unsigned int STORAGE_DIR_CHECK_INIT_INTERVAL_US = 50;
+static constexpr unsigned int STORAGE_DIR_CHECK_MAX_INTERVAL_US = 1000;
+/**
+ * Lower bound time we allow storage dir check to sleep.
+ * If it exceeds 2s, PROC_START_TIMEOUT_MSG will kill the starting app anyway,
+ * so it's fine to assume max retries is 5 mins.
+ */
+static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5;
+
/**
* A helper class containing accounting information for USAPs.
*/
@@ -814,7 +825,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode,
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
- bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
+ bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, true);
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
@@ -1458,6 +1469,31 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
}
}
+static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn) {
+ unsigned int sleepIntervalUs = STORAGE_DIR_CHECK_INIT_INTERVAL_US;
+
+ // This is just an approximate value as it doesn't need to be very accurate.
+ unsigned int sleepTotalUs = 0;
+
+ const char* dir_path = target.c_str();
+ while (sleepTotalUs < STORAGE_DIR_CHECK_TIMEOUT_US) {
+ if (access(dir_path, F_OK) == 0) {
+ return;
+ }
+ // Failed, so we add exponential backoff and retry
+ usleep(sleepIntervalUs);
+ sleepTotalUs += sleepIntervalUs;
+ sleepIntervalUs = std::min<unsigned int>(
+ sleepIntervalUs * STORAGE_DIR_CHECK_RETRY_MULTIPLIER,
+ STORAGE_DIR_CHECK_MAX_INTERVAL_US);
+ }
+ // Last chance and get the latest errno if it fails.
+ if (access(dir_path, F_OK) == 0) {
+ return;
+ }
+ fail_fn(CREATE_ERROR("Error dir is not ready %s: %s", dir_path, strerror(errno)));
+}
+
static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid,
const char* dir_name, const char* package, fail_fn_t fail_fn) {
bool hasSdcardFs = IsSdcardfsUsed();
@@ -1468,6 +1504,10 @@ static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid,
source = StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", user_id, user_id, dir_name,
package);
}
+
+ // Directory might be not ready, as prepareStorageDirs() is running asynchronously in ProcessList,
+ // so wait until dir is created.
+ WaitUntilDirReady(source, fail_fn);
std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package);
// As the parent is mounted as tmpfs, we need to create the target dir here.
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/core/jni/com_android_internal_security_VerityUtils.cpp
index dda44fb72cfc..411a392a075c 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/core/jni/com_android_internal_security_VerityUtils.cpp
@@ -19,8 +19,8 @@
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
-#include "jni.h"
#include <utils/Log.h>
+#include "jni.h"
#include <errno.h>
#include <fcntl.h>
@@ -39,7 +39,7 @@ namespace android {
namespace {
-int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
+int enableFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
ScopedUtfChars path(env, filePath);
if (path.c_str() == nullptr) {
return EINVAL;
@@ -124,11 +124,11 @@ const JNINativeMethod sMethods[] = {
{"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity},
};
-} // namespace
+} // namespace
-int register_android_server_security_VerityUtils(JNIEnv* env) {
- return jniRegisterNativeMethods(env,
- "com/android/server/security/VerityUtils", sMethods, NELEM(sMethods));
+int register_com_android_internal_security_VerityUtils(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "com/android/internal/security/VerityUtils", sMethods,
+ NELEM(sMethods));
}
-} // namespace android
+} // namespace android
diff --git a/core/jni/permission_utils.cpp b/core/jni/permission_utils.cpp
new file mode 100644
index 000000000000..2b7ef9999491
--- /dev/null
+++ b/core/jni/permission_utils.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "permission_utils.h"
+#include "core_jni_helpers.h"
+
+static struct {
+ jfieldID fieldUid; // Identity.uid
+ jfieldID fieldPid; // Identity.pid
+ jfieldID fieldPackageName; // Identity.packageName
+ jfieldID fieldAttributionTag; // Identity.attributionTag
+} javaIdentityFields;
+
+static const JNINativeMethod method_table[] = {
+ // no static methods, currently
+};
+
+int register_android_media_permission_Identity(JNIEnv* env) {
+ jclass identityClass = android::FindClassOrDie(env, "android/media/permission/Identity");
+ javaIdentityFields.fieldUid = android::GetFieldIDOrDie(env, identityClass, "uid", "I");
+ javaIdentityFields.fieldPid = android::GetFieldIDOrDie(env, identityClass, "pid", "I");
+ javaIdentityFields.fieldPackageName =
+ android::GetFieldIDOrDie(env, identityClass, "packageName", "Ljava/lang/String;");
+ javaIdentityFields.fieldAttributionTag =
+ android::GetFieldIDOrDie(env, identityClass, "attributionTag", "Ljava/lang/String;");
+
+ return android::RegisterMethodsOrDie(env, "android/media/permission/Identity", method_table,
+ NELEM(method_table));
+}
+
+namespace android::media::permission {
+
+Identity convertIdentity(JNIEnv* env, const jobject& jIdentity) {
+ Identity identity;
+
+ identity.uid = env->GetIntField(jIdentity, javaIdentityFields.fieldUid);
+ identity.pid = env->GetIntField(jIdentity, javaIdentityFields.fieldPid);
+
+ jstring packageNameStr = static_cast<jstring>(
+ env->GetObjectField(jIdentity, javaIdentityFields.fieldPackageName));
+ if (packageNameStr == nullptr) {
+ identity.packageName = std::nullopt;
+ } else {
+ identity.packageName = std::string(ScopedUtfChars(env, packageNameStr).c_str());
+ }
+
+ jstring attributionTagStr = static_cast<jstring>(
+ env->GetObjectField(jIdentity, javaIdentityFields.fieldAttributionTag));
+ if (attributionTagStr == nullptr) {
+ identity.attributionTag = std::nullopt;
+ } else {
+ identity.attributionTag = std::string(ScopedUtfChars(env, attributionTagStr).c_str());
+ }
+
+ return identity;
+}
+
+} // namespace android::media::permission
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java b/core/jni/permission_utils.h
index a554e5f583e7..d625bb6ba30a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java
+++ b/core/jni/permission_utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+#pragma once
-import java.util.function.Consumer;
+#include <android/media/permission/Identity.h>
+#include <jni.h>
-/** Accepts and retains the most recent value for verification */
-class TestableConsumer<T> implements Consumer<T> {
- T mValue;
+namespace android::media::permission {
- @Override
- public void accept(T t) {
- mValue = t;
- }
-
- public T getValue() {
- return mValue;
- }
+Identity convertIdentity(JNIEnv* env, const jobject& jIdentity);
}
+
+int register_android_media_permission_Identity(JNIEnv* env);
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index e62b5c102a59..ea5e7f729845 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,9 +14,10 @@ per-file settings_enums.proto=tmfang@google.com
# Frameworks
ogunwale@google.com
jjaggi@google.com
+kwekua@google.com
roosa@google.com
per-file package_item_info.proto = toddke@google.com
-per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
# Biometrics
diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS
index 296abd18aadc..cc479e61b855 100644
--- a/core/proto/android/app/OWNERS
+++ b/core/proto/android/app/OWNERS
@@ -1 +1 @@
-per-file location_time_zone_manager.proto = nfuller@google.com, mingaleev@google.com
+per-file location_time_zone_manager.proto, time_zone_detector.proto = nfuller@google.com, mingaleev@google.com
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
index f44d5495f132..891e9fca36aa 100644
--- a/core/proto/android/app/location_time_zone_manager.proto
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
package android.app.time;
+import "frameworks/base/core/proto/android/app/time_zone_detector.proto";
import "frameworks/base/core/proto/android/privacy.proto";
option java_multiple_files = true;
@@ -31,15 +32,6 @@ message LocationTimeZoneManagerServiceStateProto {
repeated TimeZoneProviderStateProto secondary_provider_states = 3;
}
-// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
-// detector.
-message GeolocationTimeZoneSuggestionProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- repeated string zone_ids = 1;
- repeated string debug_info = 2;
-}
-
// The state tracked for a LocationTimeZoneProvider.
message TimeZoneProviderStateProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/app/time_zone_detector.proto b/core/proto/android/app/time_zone_detector.proto
new file mode 100644
index 000000000000..b33ca1d4f476
--- /dev/null
+++ b/core/proto/android/app/time_zone_detector.proto
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.app.time;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "TimeZoneDetectorProto";
+
+// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
+// detector.
+message GeolocationTimeZoneSuggestionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated string zone_ids = 1;
+ repeated string debug_info = 2;
+}
+
+/*
+ * An obfuscated and simplified time zone suggestion for metrics use.
+ *
+ * The suggestion's time zone IDs (which relate to location) are obfuscated by
+ * mapping them to an ordinal. When the ordinal is assigned consistently across
+ * several objects (i.e. so the same time zone ID is always mapped to the same
+ * ordinal), this allows comparisons between those objects. For example, we can
+ * answer "did these two suggestions agree?", "does the suggestion match the
+ * device's current time zone?", without leaking knowledge of location. Ordinals
+ * are also significantly more compact than full IANA TZDB IDs, albeit highly
+ * unstable and of limited use.
+ */
+message MetricsTimeZoneSuggestion {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum Type {
+ CERTAIN = 1;
+ UNCERTAIN = 2;
+ }
+ optional Type type = 1;
+
+ // The ordinals for time zone(s) in the suggestion. Always empty for
+ // UNCERTAIN, and can be empty for CERTAIN, for example when the device is in
+ // a disputed area / on an ocean.
+ repeated uint32 time_zone_ordinals = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2b665c0fe9fc..a5fbae9878ac 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -513,17 +513,17 @@ message IncidentProto {
optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
(section).type = SECTION_DUMPSYS,
- (section).args = "power_stats --proto meter"
+ (section).args = "powerstats --proto meter"
];
optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
(section).type = SECTION_DUMPSYS,
- (section).args = "power_stats --proto model"
+ (section).args = "powerstats --proto model"
];
optional com.android.server.powerstats.PowerStatsServiceResidencyProto powerstats_residency = 3056 [
(section).type = SECTION_DUMPSYS,
- (section).args = "power_stats --proto residency"
+ (section).args = "powerstats --proto residency"
];
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 632d372bf7ab..dca6002d23f8 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -192,6 +192,12 @@ message SecureSettingsProto {
optional Camera camera = 12;
optional SettingProto carrier_apps_handled = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ message Clipboard {
+ optional SettingProto show_access_notifications = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional Clipboard clipboard = 89;
+
optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
repeated SettingProto completed_categories = 15;
optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -647,5 +653,5 @@ message SecureSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 89;
+ // Next tag = 90;
}
diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto
index 900235ea65fb..bbb0edd4fd59 100644
--- a/core/proto/android/server/biometrics.proto
+++ b/core/proto/android/server/biometrics.proto
@@ -125,6 +125,13 @@ message SensorStateProto {
// User states for this sensor.
repeated UserStateProto user_states = 4;
+
+ // True if resetLockout requires a HAT to be verified in the TEE or equivalent.
+ optional bool reset_lockout_requires_hardware_auth_token = 5;
+
+ // True if a HAT is required (field above) AND a challenge needs to be generated by the
+ // biometric TEE (or equivalent), and wrapped within the HAT.
+ optional bool reset_lockout_requires_challenge = 6;
}
// State of a specific user for a specific sensor.
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index e32c07f50ff2..3a959f13fea4 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -57,6 +57,8 @@ message IntervalStatsProto {
// Time attributes stored as an offset of the IntervalStats's beginTime.
optional int64 last_time_visible_ms = 10;
optional int64 total_time_visible_ms = 11;
+ // Time attributes stored as an offset of the IntervalStats's beginTime.
+ optional int64 last_time_component_used_ms = 12;
}
// Stores the relevant information an IntervalStats will have about a Configuration
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index 664c22de86a6..3e5cd92c2533 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -81,6 +81,7 @@ message UsageStatsObfuscatedProto {
optional int64 total_time_service_used_ms = 9;
optional int64 last_time_visible_ms = 10;
optional int64 total_time_visible_ms = 11;
+ optional int64 last_time_component_used_ms = 12;
}
/**
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index f26bf7cdb6c1..a7127ad79401 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -346,6 +346,7 @@ message ActivityRecordProto {
optional int32 proc_id = 29;
optional bool translucent = 30;
optional bool pip_auto_enter_enabled = 31;
+ optional bool in_size_compat_mode = 32;
}
/* represents WindowToken */
@@ -406,7 +407,7 @@ message WindowStateProto {
optional int64 finished_seamless_rotation_frame = 40;
optional WindowFramesProto window_frames = 41;
optional bool force_seamless_rotation = 42;
- optional bool in_size_compat_mode = 43;
+ optional bool has_compat_scale = 43;
optional float global_scale = 44;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f7eb3648dfc0..57d2e658d5f1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -506,6 +506,8 @@
<protected-broadcast android:name="android.app.action.ACTION_PASSWORD_FAILED" />
<protected-broadcast android:name="android.app.action.ACTION_PASSWORD_SUCCEEDED" />
<protected-broadcast android:name="com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION" />
+ <protected-broadcast android:name="com.android.server.ACTION_PROFILE_OFF_DEADLINE" />
+ <protected-broadcast android:name="com.android.server.ACTION_TURN_PROFILE_ON_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_ADDED" />
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNLOCKED" />
@@ -985,6 +987,23 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="signature|appop|preinstalled" />
+ <!-- Allows an application to modify and delete media files on this device or any connected
+ storage device without user confirmation. Applications must already be granted the
+ {@link #READ_EXTERNAL_STORAGE} or {@link #MANAGE_EXTERNAL_STORAGE}} permissions for this
+ permission to take effect.
+ <p>Even if applications are granted this permission, if applications want to modify or
+ delete media files, they also must get the access by calling
+ {@link android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)},
+ {@link android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)}, or
+ {@link android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)}.
+ <p>This permission doesn't give read or write access directly. It only prevents the user
+ confirmation dialog for these requests.
+ <p>If applications are not granted {@link #ACCESS_MEDIA_LOCATION}, the system also pops up
+ the user confirmation dialog for the write request.
+ <p>Protection level: signature|appop|preinstalled -->
+ <permission android:name="android.permission.MANAGE_MEDIA"
+ android:protectionLevel="signature|appop|preinstalled" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device location -->
<!-- ====================================================================== -->
@@ -2773,11 +2792,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|installer|appPredictor|pre23|development -->
+ <p>Protection level: signature|setup|appop|installer|appPredictor|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" />
+ android:protectionLevel="signature|setup|appop|installer|appPredictor|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -3688,6 +3707,13 @@
<permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage hotword detection on the device.
+ <p>Protection level: internal|preinstalled
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION"
+ android:protectionLevel="internal|preinstalled" />
+
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
<p>Protection level: signature
@@ -4363,6 +4389,11 @@
<permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
android:protectionLevel="signature|privileged" />
+ <!-- @TestApi Allows an application to query audio related state.
+ @hide -->
+ <permission android:name="android.permission.QUERY_AUDIO_STATE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to modify what effects are applied to all audio
(matching certain criteria) from any application.
<p>Not for use by third-party applications.</p>
@@ -5257,15 +5288,20 @@
<permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to manage speech recognition service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SPEECH_RECOGNITION"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to manage the content suggestions service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to manage the app predictions service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_APP_PREDICTIONS"
- android:protectionLevel="signature|appPredictor" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows an application to manage the search ui service.
@hide <p>Not for use by third-party applications.</p> -->
@@ -5412,6 +5448,8 @@
intents}.
<p>Protection level: normal -->
<permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:label="@string/permlab_fullScreenIntent"
+ android:description="@string/permdesc_fullScreenIntent"
android:protectionLevel="normal" />
<!-- @SystemApi Allows requesting the framework broadcast the
@@ -5593,6 +5631,10 @@
<!-- Attribution for Gnss Time Update service. -->
<attribution android:tag="GnssTimeUpdateService"
android:label="@string/gnss_time_update_service"/>
+ <!-- Attribution for MusicRecognitionManagerService.
+ <p>Not for use by third-party applications.</p> -->
+ <attribution android:tag="MusicRecognitionManagerService"
+ android:label="@string/music_recognition_manager_service"/>
<application android:process="system"
android:persistent="true"
diff --git a/core/res/res/drawable/ic_accessibility_24dp.xml b/core/res/res/drawable/ic_accessibility_24dp.xml
new file mode 100644
index 000000000000..51e695969c85
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_24dp.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="21dp"
+ android:height="21dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.5,6c-2.61,0.7 -5.67,1 -8.5,1S6.11,6.7 3.5,6L3,8c1.86,0.5 4,0.83 6,
+ 1v13h2v-6h2v6h2V9c2,-0.17 4.14,-0.5 6,-1L20.5,6zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2s-2,
+ 0.9 -2,2S10.9,6 12,6z"
+ android:fillColor="#FF000000"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
index f92e6d63cbe7..b969fa49bcc7 100644
--- a/core/res/res/layout/notification_expand_button.xml
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -39,7 +39,7 @@
android:layout_height="@dimen/notification_expand_button_pill_height"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:gravity="center_vertical"
- android:paddingLeft="8dp"
+ android:paddingStart="8dp"
/>
<ImageView
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 2d1c3422ca36..b9a3625f9e45 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -27,7 +27,7 @@
android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_action_list_height"
+ android:layout_marginBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
diff --git a/core/res/res/layout/notification_template_material_big_call.xml b/core/res/res/layout/notification_template_material_big_call.xml
new file mode 100644
index 000000000000..1d5046777e77
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_call.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.internal.widget.CallLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="call"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_template_conversation_icon_container" />
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:orientation="vertical"
+ android:minHeight="68dp"
+ >
+
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <include layout="@layout/notification_template_text_multiline" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ layout="@layout/notification_template_progress"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include
+ layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 2954ba2a0903..86e7dec29e7d 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -31,7 +31,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginTop="@dimen/notification_content_margin_top"
- android:layout_marginBottom="@dimen/notification_action_list_height"
+ android:layout_marginBottom="@dimen/notification_content_margin"
android:clipToPadding="false"
android:orientation="vertical"
>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
index 7b52ec30abe6..5d9e761842d8 100644
--- a/core/res/res/layout/notification_template_material_call.xml
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +18,6 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
android:clipChildren="false"
android:tag="call"
android:theme="@style/Theme.DeviceDefault.Notification"
@@ -29,58 +27,46 @@
<include layout="@layout/notification_template_conversation_icon_container" />
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_action_list_height"
- android:orientation="vertical"
+ android:layout_height="80dp"
+ android:orientation="horizontal"
>
<LinearLayout
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_gravity="top"
- android:orientation="horizontal"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:orientation="vertical"
+ android:minHeight="68dp"
>
- <LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/conversation_content_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
- android:orientation="vertical"
- android:minHeight="68dp"
- >
+ />
- <include
- layout="@layout/notification_template_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
+ <include layout="@layout/notification_template_text" />
- <include layout="@layout/notification_template_text" />
+ </LinearLayout>
- <include
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_progress_bar_height"
- android:layout_marginTop="@dimen/notification_progress_margin_top"
- layout="@layout/notification_template_progress"
- />
- </LinearLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
- <!-- TODO(b/179178086): remove padding from main column when this is visible -->
- <include layout="@layout/notification_expand_button"
+ <include
+ layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="top|end"
+ android:layout_gravity="center_vertical"
/>
- </LinearLayout>
-
- <include layout="@layout/notification_material_action_list" />
+ </FrameLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_text_multiline.xml b/core/res/res/layout/notification_template_text_multiline.xml
index d632ac9527d1..ec493bcc5082 100644
--- a/core/res/res/layout/notification_template_text_multiline.xml
+++ b/core/res/res/layout/notification_template_text_multiline.xml
@@ -22,7 +22,7 @@
android:layout_gravity="top"
android:layout_marginTop="@dimen/notification_text_margin_top"
android:minHeight="@dimen/notification_text_height"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:gravity="top"
android:maxLines="2"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 648d2a1034d0..7d7b73166170 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Laat die program toe om jou fotoversameling te wysig."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lees liggings in jou mediaversameling"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Laat die program toe om liggings in jou mediaversameling te lees."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Gebruik biometrie"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gebruik biometrie of skermslot"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifieer dat dit jy is"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gebruik jou biometrie om voort te gaan"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometriese hardeware is nie beskikbaar nie"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Stawing is gekanselleer"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nie herken nie"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Stawing is gekanselleer"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Geen PIN, patroon of wagwoord is gestel nie"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Kon nie staaf nie"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gebruik skermslot"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Voer jou toesteleiebewys in om voort te gaan."</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Gedeeltelike vingerafdruk is bespeur. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Vingerafdruksensor is vuil. Maak dit skoon en probeer weer."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gebruik vingerafdruk of skermslot"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gebruik jou vingerafdruk om voort te gaan"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Gesigslot word nie op hierdie toestel gesteun nie."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor is tydelik gedeaktiveer."</string>
<string name="face_name_template" msgid="3877037340223318119">"Gesig <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gebruik gesigslot"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gebruik gesig- of skermslot"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Gebruik gesigslot om voort te gaan"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Gesig-ikoon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Nuus en tydskrifte"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kaarte en navigasie"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktiwiteit"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Toeganklikheid"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Toestelberging"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-ontfouting"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"uur"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index eb98c69b92be..254ca9cfad36 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"መተግበሪያው የፎቶ ስብስብዎን እንዲቀይረው ያስችለዋል።"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"አካባቢዎችን ከሚዲያ ስብስብዎ ማንበብ"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"መተግበሪያው አካባቢዎችን ከሚዲያ ስብስብዎ እንዲያነብብ ያስችለዋል።"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ባዮሜትሪኮችን ይጠቀሙ"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ባዮሜትሪክስ ወይም ማያ ገጽ መቆለፊያን ይጠቀሙ"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"እርስዎን መሆንዎን ያረጋግጡ"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ለመቀጠል ባዮሜትሪክዎን ይጠቀሙ"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ባዮሜትራዊ ሃርድዌር አይገኝም"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ማረጋገጥ ተሰርዟል"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"አልታወቀም"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ማረጋገጥ ተሰርዟል"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ምንም ፒን፣ ሥርዓተ ጥለት ወይም የይለፍ ቃል አልተቀናበረም"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ማረጋገጥ ላይ ስህተት"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"የማያ ገጽ መቆለፊን ይጠቀሙ"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ለመቀጠል የመሣሪያዎን የመግቢያ ማስረጃ ያስገቡ"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ከፊል የጣት አሻራ ተገኝቷል። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"የጣት አሻራ ዳሳሽ ቆሽሿል። እባክዎ ያጽዱት እና እንደገና ይሞክሩ።"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"የጣት አሻራ ይጠቀሙ"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"የጣት አሻራ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"በመልክ መክፈት መስጫ በዚህ መሣሪያ ላይ አይደገፍም።"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string>
<string name="face_name_template" msgid="3877037340223318119">"ፊት <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"በመልክ መክፈትን ይጠቀሙ"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"የመልክ ወይም የማያ ገጽ መቆለፊያን ይጠቀሙ"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ለመቀጠል በመልክ መክፈትን ይጠቀሙ"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"የፊት አዶ"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ዜና እና መጽሔቶች"</string>
<string name="app_category_maps" msgid="6395725487922533156">"ካርታዎች እና ዳሰሳ"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ውጤታማነት"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ተደራሽነት"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"የመሣሪያ ማከማቻ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"የዩኤስቢ ማረሚያ"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ሰዓት"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d6759b6453b8..89ed573fd1f0 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -562,23 +562,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"للسماح للتطبيق بتعديل مجموعة صورك."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"قراءة المواقع من مجموعة الوسائط التابعة لك"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"للسماح للتطبيق بقراءة المواقع من مجموعة الوسائط التابعة لك."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"استخدام المقاييس الحيوية"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استخدام المقاييس الحيوية أو قفل الشاشة"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"إثبات هويتك"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"استخدام المقاييس الحيوية للمتابعة"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"معدّات المقاييس الحيوية غير متاحة."</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"تم إلغاء المصادقة."</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"لم يتم التعرف عليها."</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"تم إلغاء المصادقة."</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"لم يتم ضبط رقم تعريف شخصي أو نقش أو كلمة مرور."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"خطأ في المصادقة"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"استخدام قفل الشاشة"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"إدخال بيانات اعتماد الجهاز للمتابعة"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"تم اكتشاف جزء من بصمة الإصبع فقط؛ يرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"زر استشعار بصمات الأصابع متّسخ. يُرجى تنظيفه وإعادة المحاولة."</string>
@@ -601,10 +596,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استخدام بصمة الإصبع"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"استخدام بصمة الإصبع أو قفل الشاشة"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"يمكنك استخدام بصمة الإصبع للمتابعة."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -650,12 +643,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"\"فتح القفل بالوجه\" غير متوفر على هذا الجهاز."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
<string name="face_name_template" msgid="3877037340223318119">"الوجه <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"استخدام ميزة \"فتح القفل بالوجه\""</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"استخدام ميزة \"فتح القفل بالوجه\" أو ميزة \"قفل الشاشة\""</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"استخدام ميزة \"فتح القفل بالوجه\" للمتابعة"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"رمز الوجه"</string>
@@ -2018,7 +2008,7 @@
<string name="notification_phishing_alert_content_description" msgid="494227305355958790">"تنبيه بشأن تصيّد احتيالي"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"الملف الشخصي للعمل"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"تمّ تفعيل التنبيه"</string>
- <string name="notification_verified_content_description" msgid="6401483602782359391">"تم التحقّق"</string>
+ <string name="notification_verified_content_description" msgid="6401483602782359391">"تم التحقّق من المتّصل"</string>
<string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"توسيع"</string>
<string name="expand_button_content_description_expanded" msgid="7484217944948667489">"تصغير"</string>
<string name="expand_action_accessibility" msgid="1947657036871746627">"تبديل التوسيع"</string>
@@ -2094,8 +2084,7 @@
<string name="app_category_news" msgid="1172762719574964544">"الأخبار والمجلات"</string>
<string name="app_category_maps" msgid="6395725487922533156">"الخرائط والتنقل"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"الإنتاجية"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"أدوات تمكين الوصول"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"مساحة التخزين للجهاز"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"‏تصحيح أخطاء USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ساعة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 0a4c397de8c3..67d2ce05a348 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"এপক আপোনাৰ ফট’ সংগ্ৰহ সালসলনি কৰিবলৈ দিয়ে।"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"এপক আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ দিয়ে।"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"বায়\'মেট্ৰিক ব্যৱহাৰ কৰক"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"বায়\'মেট্ৰিক অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"এইয়া আপুনিয়েই বুলি সত্যাপন কৰক"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"অব্যাহত ৰাখিবলৈ আপোনাৰ বায়\'মেট্ৰিক ব্যৱহাৰ কৰক"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"বিশ্বাসযোগ্যতাৰ প্ৰমাণীকৰণ বাতিল কৰা হৈছে"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"চিনাক্ত কৰিব পৰা নাই"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"বিশ্বাসযোগ্যতাৰ প্ৰমাণীকৰণ বাতিল কৰা হৈছে"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"কোনো পিন, আৰ্হি বা পাছৱৰ্ড ছেট কৰা নাই"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"আসোঁৱাহৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰি থকা হৈছে"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"স্ক্ৰীন ল\'ক ব্যৱহাৰ কৰক"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"অব্যাহত ৰাখিবলৈ আপোনাৰ ডিভাইচৰ ক্ৰেডেনশ্বিয়েল দিয়ক"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ফিংগাৰপ্ৰিণ্ট আংশিকভাৱে চিনাক্ত কৰা হৈছে। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ফিগাৰপ্ৰিণ্টৰ প্ৰক্ৰিয়া সম্পাদন কৰিবপৰা নগ\'ল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো লেতেৰা হৈ আছে। অনুগ্ৰহ কৰি পৰিষ্কাৰ কৰি আকৌ চেষ্টা কৰক।"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ফিংগাৰপ্ৰিণ্ট অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"অব্যাহত ৰাখিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"এই ডিভাইচটোত মুখাৱয়বৰদ্বাৰা আনলক কৰা সুবিধাটো নচলে।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
<string name="face_name_template" msgid="3877037340223318119">"মুখমণ্ডল <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"মুখাৱয়বৰে আনলক কৰা ব্যৱহাৰ কৰক"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"মুখাৱয়বৰে আনলক কৰা অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"অব্যাহত ৰাখিবলৈ মুখাৱয়বৰে আনলক কৰা ব্যৱহাৰ কৰক"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"মুখমণ্ডলৰ আইকন"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"বাতৰি আৰু আলোচনী"</string>
<string name="app_category_maps" msgid="6395725487922533156">"মেপ আৰু দিক্-নিৰ্দেশনা"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"উৎপাদনশীলতা"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"সাধ্য সুবিধা"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ডিভাইচৰ সঞ্চয়াগাৰ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"ইউএছবি ডিবাগিং"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ঘণ্টা"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index afb5c397dde5..76b5f1b9aded 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Tətbiqin foto kolleksiyanıza düzəliş etməsinə icazə verir."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"media kolleksiyanızdan məkanları oxuyun"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Tətbiqin media kolleksiyanızdan məkanları oxumasına icazə verin."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biometrik məlumatlardan istifadə edin"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrik məlumatlardan və ya ekran kilidindən istifadə edin"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Kimliyinizi doğrulayın"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Davam etmək üçün biometrik məlumatlarınızdan istifadə edin"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrik proqram əlçatan deyil"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Doğrulama ləğv edildi"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tanınmır"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Doğrulama ləğv edildi"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Pin, nümunə və ya parol ayarlanmayıb"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Doğrulama zamanı xəta baş verdi"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekran kilidindən istifadə edin"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Davam etmək üçün cihazın giriş məlumatlarını daxil edin"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Barmaq izi yarımçıq müəyyən olundu. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Barmaq izi sensoru çirklidir. Lütfən, təmizləyin və yenidən cəhd edin."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izindən istifadə edin"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmaq izi və ya ekran kilidindən istifadə edin"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Davam etmək üçün barmaq izinizi istifadə edin"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Üz kilidi bu cihazda dəstəklənmir."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor müvəqqəti deaktivdir."</string>
<string name="face_name_template" msgid="3877037340223318119">"Üz <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Üz ilə kiliddən çıxarmadan istifadə edin"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Üz və ya ekran kilidindən istifadə edin"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Davam etmək üçün üz ilə kiliddən çıxarmadan istifadə edin"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Üz işarəsi"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Xəbər və Jurnallar"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Xəritə və Naviqasiya"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Məhsuldarlıq"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Əlçatımlılıq"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Cihaz yaddaşı"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB sazlama"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"saat"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0d23fa56d55b..97781c7849a9 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -553,23 +553,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Dozvoljava aplikaciji da menja kolekciju slika."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"čitanje lokacija iz medijske kolekcije"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Dozvoljava aplikaciji da čita lokacije iz medijske kolekcije."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Koristite biometriju"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Koristite biometriju ili zaključavanje ekrana"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite svoj identitet"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Koristite biometrijski podatak da biste nastavili"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Potvrda identiteta je otkazana"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Potvrda identiteta je otkazana"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Niste podesili ni PIN, ni šablon, ni lozinku"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Greška pri potvrdi identiteta"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Koristite zaključavanje ekrana"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Unesite akreditiv za uređaj da biste nastavili"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Otkriven je delimični otisak prsta. Probajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nije uspela obrada otiska prsta. Probajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Senzor za otiske prstiju je prljav. Očistite ga i pokušajte ponovo."</string>
@@ -592,10 +587,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristite otisak prsta ili zaključavanje ekrana"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -641,12 +634,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Otključavanje licem nije podržano na ovom uređaju"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je privremeno onemogućen."</string>
<string name="face_name_template" msgid="3877037340223318119">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Koristite otključavanje licem"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Koristite zaključavanje licem ili zaključavanje ekrana"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Koristite otključavanje licem da biste nastavili"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona lica"</string>
@@ -1998,8 +1988,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Novosti i časopisi"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mape i navigacija"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivnost"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Pristupačnost"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Memorijski prostor uređaja"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Otklanjanje grešaka sa USB-a"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"sat"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 87b4ce7faa5a..32672472f145 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Праграма зможа змяняць фотакалекцыю."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"паказваць месцазнаходжанне ў калекцыі мультымедыя"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Праграма зможа паказваць месцазнаходжанне ў калекцыі мультымедыя."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Выкарыстоўваць біяметрыю"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Выкарыстоўваць біяметрыю ці блакіроўку экрана"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Спраўдзіце, што гэта вы"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Каб працягнуць, скарыстайце свае біяметрычныя даныя"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Біяметрычнае абсталяванне недаступнае"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аўтэнтыфікацыя скасавана"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не распазнана"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аўтэнтыфікацыя скасавана"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не заданы PIN-код, узор разблакіроўкі або пароль"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Памылка аўтэнтыфікацыі"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ужываць блакіроўку экрана"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Каб працягнуць, увядзіце ўліковыя даныя вашай прылады"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Адсканіравана толькі частка адбітка пальца. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сканер адбіткаў пальцаў брудны. Ачысціце яго і паспрабуйце яшчэ раз."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Выкарыстоўваць адбітак пальца"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Выкарыстоўваць адбітак пальца ці блакіроўку экрана"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Каб працягнуць, выкарыстоўвайце свой адбітак пальца"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"На гэтай прыладзе распазнаванне твару не падтрымліваецца."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Датчык часова выключаны."</string>
<string name="face_name_template" msgid="3877037340223318119">"Твар <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Ужываць распазнаванне твару"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Выкарыстоўваць распазнаванне твару ці блакіроўку экрана"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Каб працягнуць, скарыстайце распазнаванне твару"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Значок твару"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Навіны і часопісы"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карты і навігацыя"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Прадукцыйнасць"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Спецыяльныя магчымасці"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Сховішча на прыладзе"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Адладка USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"гадз"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 7e847e0a66af..ac55c913a64e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Разрешава на приложението да променя колекцията ви от снимки."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"да чете местоположенията от мултимедийната ви колекция"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Разрешава на приложението да чете местоположенията от мултимедийната ви колекция."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Използване на биометр. данни"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Използване на биометрични данни или опцията за заключване на екрана"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Потвърдете, че сте вие"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Използвайте биометричните си данни, за да продължите"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометричният хардуер не е налице"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Удостоверяването бе анулирано"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не е разпознато"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Удостоверяването бе анулирано"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Няма зададен ПИН код, фигура или парола"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при удостоверяването"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ползване на заключв. на екрана"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Въведете идентификационните данни на устройството, за да продължите"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Открит е частичен отпечатък. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатъкът не бе обработен. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сензорът за отпечатъци е мръсен. Моля, почистете го и опитайте отново."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Използване на отпечатък"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Използване на отпечатък или опцията за заключване на екрана"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Използвайте отпечатъка си, за да продължите"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Отключването с лице не се поддържа на това устройство."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Сензорът е временно деактивиран."</string>
<string name="face_name_template" msgid="3877037340223318119">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Използване на отключв. с лице"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Използване на отключването с лице или опцията за заключване на екрана"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Използвайте функцията за отключване с лице, за да продължите"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Икона на лице"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Новини и списания"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карти и навигация"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Производителност"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Достъпност"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Хранилище на устройството"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отстраняване на грешки през USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"час"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 4d3d79394521..ff25441fbe7d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"অ্যাপকে আপনার ফটো সংগ্রহ পরিবর্তন করার অনুমতি দিন।"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ডিয়া সংগ্রহ থেকে লোকেশন দেখতে দিন"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"আপনার মিডিয়া সংগ্রহ থেকে লোকেশন দেখতে অ্যাপকে অনুমতি দিন।"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"বায়োমেট্রিক্স ব্যবহার করুন"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"বায়োমেট্রিক্স অথবা স্ক্রিন লক ব্যবহার করুন"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"আপনার পরিচয় যাচাই করুন"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"চালিয়ে যেতে আপনার বায়োমেট্রিক্স ব্যবহার করুন"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"যাচাইকরণ বাতিল হয়েছে"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"স্বীকৃত নয়"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"যাচাইকরণ বাতিল হয়েছে"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"পিন, প্যাটার্ন অথবা পাসওয়ার্ড সেট করা নেই"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"যাচাইকরণে সমস্যা হয়েছে"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"স্ক্রিন লক ব্যবহার করুন"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"চালিয়ে যেতে আপনার ডিভাইসের ক্রেডেনশিয়াল ব্যবহার করুন"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"আঙ্গুলের ছাপ আংশিক শনাক্ত করা হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"আঙ্গুলের ছাপ নেওয়ার সেন্সরটি অপরিস্কার৷ পরিষ্কার করে আবার চেষ্টা করুন৷"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"আঙ্গুলের ছাপ ব্যবহার করুন"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"আঙ্গুলের ছাপ অথবা স্ক্রিন লক ব্যবহার করুন"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"চালিয়ে যেতে আঙ্গুলের ছাপ ব্যবহার করুন"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"এই ডিভাইসে মুখের সাহায্যে আনলক করার সুবিধাটি কাজ করে না।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> ফেস"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"মুখের সাহায্যে আনলক ব্যবহার করুন"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"মুখ অথবা স্ক্রিন লক ব্যবহার করুন"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"চালিয়ে যেতে মুখের সাহায্যে আনলক ব্যবহার করুন"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ফেস আইকন"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"খবর ও পত্রিকাগুলি"</string>
<string name="app_category_maps" msgid="6395725487922533156">"ম্যাপ ও নেভিগেশান"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"উৎপাদনশীলতা"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"অ্যাক্সেসিবিলিটি"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ডিভাইসের স্টোরেজ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ডিবাগিং"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ঘণ্টা"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 2e24175bbaa4..25129e489a05 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -553,23 +553,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Omogućava aplikaciji da mijenja vašu kolekciju fotografija."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"čitanje lokacija iz kolekcije medija"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Omogućava aplikaciji da čita lokacije iz vaše kolekcije medija."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Koristi biometriju"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Koristi biometriju ili zaključavanje ekrana"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite identitet"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Koristite biometriju da nastavite"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikacija je otkazana"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikacija je otkazana"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nije postavljen PIN, uzorak niti lozinka"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Greška pri autentifikaciji"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Koristi zaključavanje ekrana"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Unesite akreditiv uređaja da nastavite"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Otkriven je djelimični otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Senzor za otisak prsta je prljav. Očistite ga i pokušajte ponovo."</string>
@@ -592,10 +587,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristi otisak prsta ili zaključavanje ekrana"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -641,12 +634,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Otključavanje licem nije podržano na ovom uređaju."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je privremeno onemogućen."</string>
<string name="face_name_template" msgid="3877037340223318119">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Koristi otključavanje licem"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Koristi otključavanje licem ili zaključavanje ekrana"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Koristite otključavanje licem da nastavite"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona lica"</string>
@@ -1998,8 +1988,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Vijesti i časopisi"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mape i navigacija"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivnost"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Pristupačnost"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Memorija uređaja"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Otklanjanje grešaka putem USB-a"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"sat"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 70caa2cff7fe..6f2af9203553 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permet que l\'aplicació modifiqui la teva col·lecció de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"llegir les ubicacions de les teves col·leccions multimèdia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permet que l\'aplicació llegeixi les ubicacions de les teves col·leccions multimèdia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Utilitza la biometria"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Fes servir la biometria o el bloqueig de pantalla"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica que ets tu"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilitza la teva biometria per continuar"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Maquinari biomètric no disponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"S\'ha cancel·lat l\'autenticació"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"No s\'ha reconegut"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"S\'ha cancel·lat l\'autenticació"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No s\'ha definit cap PIN, patró o contrasenya"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error en l\'autenticació"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utilitza el bloqueig de pantalla"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Utilitza les credencials del teu dispositiu per continuar"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"El sensor d\'empremtes dactilars està brut. Neteja\'l i torna-ho a provar."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes dactilars."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilitza l\'empremta digital o el bloqueig de pantalla"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Fes servir l\'empremta digital per continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"El desbloqueig facial no és compatible amb el dispositiu."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"El sensor està desactivat temporalment."</string>
<string name="face_name_template" msgid="3877037340223318119">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Utilitza el desbloqueig facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utilitza el desbloqueig facial o de pantalla"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Utilitza el desbloqueig facial per continuar"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Icona facial"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Notícies i revistes"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapes i navegació"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivitat"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibilitat"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Emmagatzematge del dispositiu"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuració per USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0f732669f467..bd9866279e96 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Umožňuje aplikaci upravit vaši sbírku fotek."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"čtení míst ze sbírky médií"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Umožňuje aplikaci číst místa z vaší sbírky médií."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Použít biometrii"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Použít biometrii nebo zámek obrazovky"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrďte, že jste to vy"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Pokračujte biometrickým ověřením"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardware není k dispozici"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ověření bylo zrušeno"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nerozpoznáno"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Ověření bylo zrušeno"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Není nastaven žádný PIN, gesto ani heslo"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Při ověřování došlo k chybě"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Použít zámek obrazovky"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Pokračujte zadáním identifikačních úřadů svého zařízení"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Byla zjištěna jen část otisku prstu. Zkuste to znovu."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Senzor otisků prstů je znečištěn. Vyčistěte jej a zkuste to znovu."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použít otisk prstu nebo zámek obrazovky"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Pokračujte přiložením prstu"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Odemknutí obličejem na tomto zařízení není podporováno."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je dočasně deaktivován."</string>
<string name="face_name_template" msgid="3877037340223318119">"Obličej <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Použít odemknutí obličejem"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Použít odemknutí obličejem nebo zámek obrazovky"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Pokračujte odemknutím obličejem"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona obličeje"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Zprávy a časopisy"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapy a navigace"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivita"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Přístupnost"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Úložiště zařízení"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Ladění přes USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hodina"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 13afaf251317..70492e01fc1b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -552,23 +552,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Tillader, at appen kan ændre din billedsamling."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"læse placeringer fra din mediesamling"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Tillader, at appen kan læse placeringer fra din mediesamling."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Brug biometriske systemer"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Brug biometriske systemer eller skærmlås"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Bekræft, at det er dig"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Brug dine biometriske data for at fortsætte"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk hardware er ikke tilgængelig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Godkendelsen blev annulleret"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ikke genkendt"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Godkendelsen blev annulleret"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Der er ikke angivet pinkode, mønster eller adgangskode"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Der opstod fejl i forbindelse med godkendelse"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Brug skærmlås"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Angiv dine loginoplysninger for enheden for at fortsætte"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingeraftrykslæseren er beskidt. Tør den af, og prøv igen."</string>
@@ -591,10 +586,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Brug fingeraftryk eller skærmlås"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Brug dit fingeraftryk for at fortsætte"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -640,12 +633,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Ansigtslås understøttes ikke på denne enhed."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensoren er midlertidigt deaktiveret."</string>
<string name="face_name_template" msgid="3877037340223318119">"Ansigt <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Brug ansigtslås"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Brug ansigts- eller skærmlås"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Brug ansigtslås for at fortsætte"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ansigt"</string>
@@ -1968,8 +1958,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Aviser og blade"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kort og navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivitet"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Hjælpefunktioner"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Lagerplads på enheden"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-fejlretning"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"time"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 512b3a555eec..6e458fa30fd5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Ermöglicht der App, deine Fotosammlung zu ändern."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"Standorte aus meiner Mediensammlung abrufen"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Ermöglicht der App, Standorte aus deiner Mediensammlung abzurufen."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biometrisches Verfahren nutzen"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrisches Verfahren oder Displaysperre verwenden"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Deine Identität bestätigen"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Mithilfe eines biometrischen Verfahrens fortfahren"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrische Hardware nicht verfügbar"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentifizierung abgebrochen"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nicht erkannt"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentifizierung abgebrochen"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Keine PIN, kein Muster und kein Passwort festgelegt"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Fehler bei der Authentifizierung"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Displaysperre verwenden"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Zum Fortfahren Anmeldedaten des Geräts eingeben"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Fingerabdruck nur teilweise erkannt. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es noch einmal."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Fingerabdruck oder Displaysperre verwenden"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Mithilfe deines Fingerabdrucks fortfahren"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock wird auf diesem Gerät nicht unterstützt."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Der Sensor ist vorübergehend deaktiviert."</string>
<string name="face_name_template" msgid="3877037340223318119">"Gesicht <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Face Unlock verwenden"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Face Unlock oder Displaysperre verwenden"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Zum Fortfahren Face Unlock verwenden"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Gesichtssymbol"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Nachrichten &amp; Zeitschriften"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Karten &amp; Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Effizienz"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Bedienungshilfen"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Gerätespeicher"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-Fehlerbehebung"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"Stunde"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d9dc2c55cb09..920f09eb777d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Επιτρέπει στην εφαρμογή να τροποποιήσει τη συλλογή φωτογραφιών σας."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ανάγνωση τοποθεσιών από τη συλλογή πολυμέσων σας"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Επιτρέπει στην εφαρμογή να διαβάσει τοποθεσίες από τη συλλογή πολυμέσων σας."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Χρήση βιομετρικών"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Χρήση βιομετρικών ή κλειδώματος οθόνης"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Επαλήθευση ταυτότητας"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Χρησιμοποιήστε βιομετρικά για να συνεχίσετε"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Δεν υπάρχει διαθέσιμος βιομετρικός εξοπλισμός"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ο έλεγχος ταυτότητας ακυρώθηκε"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Δεν αναγνωρίστηκε"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Ο έλεγχος ταυτότητας ακυρώθηκε"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Δεν έχει οριστεί PIN, μοτίβο ή κωδικός πρόσβασης"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Σφάλμα κατά τον έλεγχο ταυτότητας"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Χρήση κλειδώματος οθόνης"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Εισαγάγετε το διαπιστευτήριο της συσκευής σας για να συνεχίσετε"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Εντοπίστηκε μόνο μέρος του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Δεν ήταν δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Ο αισθητήρας δακτυλικού αποτυπώματος δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Χρήση δακτυλικού αποτυπώματος"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Χρήση δακτυλικού αποτυπώματος ή κλειδώματος οθόνης"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Χρησιμοποιήστε το δακτυλικό αποτύπωμά σας για να συνεχίσετε"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Το Face Unlock δεν υποστηρίζεται σε αυτήν τη συσκευή."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
<string name="face_name_template" msgid="3877037340223318119">"Πρόσωπο <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Χρήση Face Unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Χρήση προσώπου ή κλειδώματος οθόνης"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Χρησιμοποιήστε το Face Unlock για να συνεχίσετε"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Εικ. προσ."</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Ειδήσεις και περιοδικά"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Χάρτες και πλοήγηση"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Παραγωγικότητα"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Προσβασιμότητα"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Αποθηκευτικός χώρος συσκευής"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Εντοπισμός σφαλμάτων USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ώρα"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 73e2ca0d3fe5..43a7fb6662d6 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Allows the app to read locations from your media collection."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Use biometrics"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Enter your device credential to continue"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Use face unlock to continue"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Face icon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"News &amp; Magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivity"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibility"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Device storage"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB debugging"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hour"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 67130e1fb5c5..04390c74bb32 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Allows the app to read locations from your media collection."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Use biometrics"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Enter your device credential to continue"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Use face unlock to continue"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Face icon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"News &amp; Magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivity"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibility"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Device storage"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB debugging"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hour"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1d36494b7b8b..84224ed85d10 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Allows the app to read locations from your media collection."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Use biometrics"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Enter your device credential to continue"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Use face unlock to continue"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Face icon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"News &amp; Magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivity"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibility"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Device storage"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB debugging"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hour"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 016c55c3352c..d740e01c502e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Allows the app to read locations from your media collection."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Use biometrics"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Enter your device credential to continue"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock is not supported on this device."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporarily disabled."</string>
<string name="face_name_template" msgid="3877037340223318119">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Use face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Use face or screen lock"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Use face unlock to continue"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Face icon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"News &amp; Magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivity"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibility"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Device storage"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB debugging"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hour"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index bd7da803e42b..6125c167fa7d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite que la app modifique tu colección de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"leer ubicaciones de tu colección de contenido multimedia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que la app lea las ubicaciones de tu colección de contenido multimedia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Usar datos biométricos"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar datos biométricos o bloqueo de pantalla"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Comprueba que eres tú"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Usa tus datos biométricos para continuar"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"No hay hardware biométrico disponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Se canceló la autenticación"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"No se reconoció"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Se canceló la autenticación"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No se estableció ningún PIN, patrón ni contraseña"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error de autenticación"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueo de pantalla"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Ingresa las credenciales de tu dispositivo para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Se detectó parcialmente la huella dactilar. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No se pudo procesar la huella dactilar. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"El sensor de huellas dactilares está sucio. Limpia el sensor y vuelve a intentarlo."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar bloqueo de huella dactilar o pantalla"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utiliza tu huella dactilar para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"No se admite el desbloqueo facial en este dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Se inhabilitó temporalmente el sensor."</string>
<string name="face_name_template" msgid="3877037340223318119">"Rostro <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usar desbloqueo facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar bloqueo facial o de pantalla"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Usa el desbloqueo facial para continuar"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ícono cara"</string>
@@ -1576,8 +1566,8 @@
<string name="expires_on" msgid="1623640879705103121">"Expira el:"</string>
<string name="serial_number" msgid="3479576915806623429">"Número de serie:"</string>
<string name="fingerprints" msgid="148690767172613723">"Huellas digitales:"</string>
- <string name="sha256_fingerprint" msgid="7103976380961964600">"Huella dactilar SHA-256"</string>
- <string name="sha1_fingerprint" msgid="2339915142825390774">"Huella dactilar SHA-1:"</string>
+ <string name="sha256_fingerprint" msgid="7103976380961964600">"Huella digital SHA-256"</string>
+ <string name="sha1_fingerprint" msgid="2339915142825390774">"Huella digital SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Ver todas"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Elige actividad"</string>
<string name="share_action_provider_share_with" msgid="1904096863622941880">"Compartir con"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Noticias y revistas"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapas y navegación"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productividad"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accesibilidad"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Almacenamiento del dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuración por USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d771977431c3..0e163d2b263b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite que la aplicación modifique tu colección de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"leer las ubicaciones de tu colección de contenido multimedia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que la aplicación lea las ubicaciones de tu colección de contenido multimedia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Usar biometría"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar biometría o bloqueo de pantalla"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica que eres tú"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Usa tu biometría para continuar"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico no disponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticación cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"No se reconoce"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticación cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No se ha definido el PIN, el patrón o la contraseña"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"No se ha podido autenticar"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueo de pantalla"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Introduce las credenciales del dispositivo para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Se ha detectado una huella digital parcial. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"El sensor de huellas digitales está sucio. Límpialo y vuelve a intentarlo."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar huella digital o bloqueo de pantalla"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Usa tu huella digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"El desbloqueo facial no está disponible en este dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"El sensor está inhabilitado en estos momentos."</string>
<string name="face_name_template" msgid="3877037340223318119">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usar desbloqueo facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar desbloqueo facial o bloqueo de pantalla"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Usa el desbloqueo facial para continuar"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Icono cara"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Noticias y revistas"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapas y navegación"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productividad"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accesibilidad"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Almacenamiento del dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuración por USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index ec02fad6683c..9f6df6e8ff35 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Võimaldab rakendusel muuta teie fotokogu."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"Lugeda teie meediakogus olevaid asukohti"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Võimaldab rakendusel lugeda teie meediakogus olevaid asukohti."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biomeetria kasutamine"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biomeetria või ekraaniluku kasutamine"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Kinnitage oma isik"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Jätkamiseks kasutage biomeetriat"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biomeetriline riistvara ei ole saadaval"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentimine tühistati"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ei tuvastatud"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentimine tühistati"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-koodi, mustrit ega parooli pole määratud"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Viga autentimisel"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekraaniluku kasutamine"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Jätkamiseks sisestage seadme mandaat"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Tuvastati osaline sõrmejälg. Proovige uuesti."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Sõrmejäljeandur on must. Puhastage see ja proovige uuesti."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sõrmejälje või ekraaniluku kasutamine"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Jätkamiseks kasutage sõrmejälge"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Seade ei toeta Face Unlocki."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Andur on ajutiselt keelatud."</string>
<string name="face_name_template" msgid="3877037340223318119">"Nägu <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Face Unlocki kasutamine"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Face Unlocki või ekraaniluku kasutamine"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Jätkamiseks kasutage Face Unlocki"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Näoikoon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Uudised ja ajakirjad"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kaardid ja navigeerimine"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktiivsus"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Juurdepääsetavus"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Seadme salvestusruum"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB silumine"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"tund"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 8ee77a600739..72d083363594 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -313,8 +313,8 @@
<string name="permgroupdesc_storage" msgid="6351503740613026600">"atzitu gailuko argazkiak, multimedia-edukia eta fitxategiak"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Mikrofonoa"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"grabatu audioa"</string>
- <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Ariketa fisikoa"</string>
- <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ariketa fisikoak atzitu"</string>
+ <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Jarduera fisiko"</string>
+ <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"jarduera fisikoa atzitu"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"atera argazkiak eta grabatu bideoak"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Deien erregistroa"</string>
@@ -445,8 +445,8 @@
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Aplikazioak edonoiz erabil dezake mikrofonoa audioa grabatzeko."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"bidali aginduak SIM txartelera"</string>
<string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM txartelera aginduak bidaltzeko aukera ematen die aplikazioei. Oso arriskutsua da."</string>
- <string name="permlab_activityRecognition" msgid="1782303296053990884">"hauteman ariketa fisikoa"</string>
- <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aplikazioak ariketa fisikoa hauteman dezake."</string>
+ <string name="permlab_activityRecognition" msgid="1782303296053990884">"hauteman jarduera fisiko"</string>
+ <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aplikazioak jarduera fisiko hauteman dezake."</string>
<string name="permlab_camera" msgid="6320282492904119413">"atera argazkiak eta grabatu bideoak"</string>
<string name="permdesc_camera" msgid="5240801376168647151">"Aplikazioak abian den bitartean erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
<string name="permlab_backgroundCamera" msgid="7549917926079731681">"Argazkiak atera eta bideoak grabatu atzeko planoan."</string>
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Argazki-bilduma aldatzeko baimena ematen die aplikazioei."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"multimedia-edukien bildumako kokapena irakurri"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Multimedia-edukien bildumako kokapena irakurtzeko baimena ematen die aplikazioei."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Erabili sistema biometrikoak"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Erabili sistema biometrikoak edo pantailaren blokeoa"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Egiaztatu zeu zarela"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Aurrera egiteko, erabili sistema biometrikoak"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrikoa ez dago erabilgarri"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Utzi da autentifikazioa"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ez da ezagutu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Utzi egin da autentifikazioa"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ez da ezarri PIN koderik, eredurik edo pasahitzik"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Errorea autentifikatzean"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Erabili pantailaren blokeoa"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Aurrera egiteko, idatzi gailuaren kredentzialak"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Hatz-marka ez da osorik hauteman. Saiatu berriro."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ezin izan da prozesatu hatz-marka. Saiatu berriro."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Hatz-marken sentsorea zikina dago. Garbi ezazu, eta saiatu berriro."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> hatza"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Erabili hatz-marka edo pantailaren blokeoa"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Aurrera egiteko, erabili hatz-marka"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Gailu honek ez du onartzen aurpegiaren bidez desblokeatzea."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sentsorea aldi baterako desgaitu da."</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> aurpegia"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Erabili aurpegiaren bidez desblokeatzeko eginbidea"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Erabili aurpegia edo pantailaren blokeoa"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Aurrera egiteko, erabili aurpegiaren bidez desblokeatzeko eginbidea"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Aurpegiaren ikonoa"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Albisteak eta aldizkariak"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapak eta nabigazioa"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktibitatea"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Erabilerraztasuna"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Gailuaren memoria"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB bidezko arazketa"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ordu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 42b8c85f7eb9..76d0c1e794f9 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"به برنامه اجازه می‌دهد مجموعه عکستان را تغییر دهد."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"خواندن مکان‌ها از مجموعه رسانه شما"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"به برنامه اجازه می‌دهد مکان‌ها را از مجموعه رسانه‌تان بخواند."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"استفاده از زیست‌سنجشی"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استفاده از زیست‌سنجشی یا قفل صفحه"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شما هستید"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"برای ادامه، از زیست‌سنجشی استفاده کنید"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"سخت‌افزار زیست‌سنجی دردسترس نیست"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"اصالت‌سنجی لغو شد"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"شناسایی نشد"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"اصالت‌سنجی لغو شد"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"پین، الگو یا گذرواژه‌ای تنظیم نشده است"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام اصالت‌سنجی"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"از قفل صفحه استفاده کنید"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"برای ادامه، اطلاعات کاربری دستگاهتان را وارد کنید"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر به‌طور موقت غیرفعال است."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استفاده از اثر انگشت"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"استفاده از اثر انگشت یا قفل صفحه"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"برای ادامه، از اثر انگشتتان استفاده کنید"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"«بازگشایی با چهره» در این دستگاه پشتیبانی نمی‌شود."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"حسگر به‌طور موقت غیرفعال است."</string>
<string name="face_name_template" msgid="3877037340223318119">"چهره <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"استفاده از «بازگشایی با چهره»"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"استفاده از قفل صفحه یا چهره"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"برای ادامه، از «بازگشایی با چهره» استفاده کنید"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"نماد چهره"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"اخبار و مجله"</string>
<string name="app_category_maps" msgid="6395725487922533156">"نقشه و پیمایش"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"بهره‌وری"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"دسترس‌پذیری"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"فضای ذخیره‌سازی دستگاه"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"‏اشکال‌زدایی USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ساعت"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index be0cd2358b8b..2e0d6cddc3d4 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Antaa sovelluksen muokata kuvakokoelmaasi."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lukea mediakokoelmasi sijainteja"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Antaa sovelluksen lukea mediakokoelmasi sijainteja."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Käytä biometriikkaa"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Käytä biometriikkaa tai näytön lukitusta"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Vahvista henkilöllisyytesi"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Jatka käyttämällä biometriikkaa"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrinen laitteisto ei käytettävissä"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Todennus peruutettu"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ei tunnistettu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Todennus peruutettu"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-koodia, kuviota tai salasanaa ei ole asetettu"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Virhe todennuksessa"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Käytä näytön lukitusta"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Jatka lisäämällä laitteesi kirjautumistiedot"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Sormenjälki havaittiin vain osittain. Yritä uudelleen."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Sormenjäljen prosessointi epäonnistui. Yritä uudelleen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Sormenjälkitunnistin on likainen. Puhdista tunnistin ja yritä uudelleen."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Käytä sormenjälkeä tai näytön lukitusta"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Jatka sormenjäljen avulla"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Tämä laite ei tue Face Unlockia."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
<string name="face_name_template" msgid="3877037340223318119">"Kasvot <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Käytä Face Unlockia"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Käytä Face Unlockia tai näytön lukitusta"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Jatka käyttämällä Face Unlockia"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Kasvokuvake"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Uutiset ja lehdet"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kartat ja navigointi"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Tuottavuus"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Esteettömyys"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Laitteen tallennustila"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-vianetsintä"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"tunnit"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 5c26e683d354..1ebc60465f47 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Autorise l\'application à modifier votre collection de photos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lire les positions issues de votre collection multimédia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Autorise l\'application à lire les positions indiquées dans votre collection multimédia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Utiliser les données biométriques"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utiliser les données biométriques ou le verrouillage de l\'écran"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmez que c\'est vous"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilisez votre méthode d\'authentification biométrique pour continuer"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Données biométriques non reconnues"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Aucun NIP, schéma ou mot de passe défini"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erreur d\'authentification"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utiliser le verrouillage de l\'écran"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Entrez votre authentifiant d\'appareil pour continuer"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Empreinte digitale partielle détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et réessayer."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser l\'empreinte digitale ou le verrouillage de l\'écran"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilisez votre empreinte digitale pour continuer"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Cet appar. ne prend pas en charge le déverr. par reconn. faciale."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Le capteur a été désactivé temporairement."</string>
<string name="face_name_template" msgid="3877037340223318119">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Utiliser le déverrouillage par reconnaissance faciale"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utiliser la reconnaissance faciale ou le verrouillage de l\'écran"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Utilisez le déverrouillage par reconnaissance faciale pour continuer"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Icône visage"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Actualités et magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Cartes et navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivité"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibilité"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Mémoire de l\'appareil"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Débogage USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"heures"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3d8856df65bf..ce36dbc9ba21 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Autorise l\'application à modifier votre bibliothèque photo."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"consulter des positions issues de votre bibliothèque multimédia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Autorise l\'application à consulter des positions issues de votre bibliothèque multimédia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Utiliser la biométrie"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utiliser la biométrie ou le verrouillage de l\'écran"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmez votre identité"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilisez la biométrie pour continuer"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Non reconnu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Aucun code, schéma ni mot de passe n\'est défini"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erreur d\'authentification"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utiliser verrouillage écran"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Saisissez l\'identifiant de l\'appareil pour continuer"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Empreinte digitale partiellement détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Le lecteur d\'empreinte digitale est sale. Veuillez le nettoyer, puis réessayer."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser votre empreinte digitale ou le verrouillage de l\'écran"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilisez votre empreinte digitale pour continuer"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face Unlock n\'est pas compatible avec cet appareil."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Capteur temporairement désactivé."</string>
<string name="face_name_template" msgid="3877037340223318119">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Utiliser Face Unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utiliser Face Lock ou le verrouillage de l\'écran"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Utilisez Face Unlock pour continuer"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Icône visage"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Actualités et magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Plans et navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivité"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibilité"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Mémoire de l\'appareil"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Débogage USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"heures"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 1c5ab13035f3..9d95af8badc2 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite que a aplicación modifique a túa colección de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ler localizacións da túa colección multimedia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que a aplicación lea as localizacións da túa colección multimedia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Utilizar desbloqueo biométrico"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utilizar desbloqueo biométrico ou credencial do dispositivo"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica que es ti"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Para continuar, utiliza o desbloqueo biométrico"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"O hardware biométrico non está dispoñible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Cancelouse a autenticación"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Non se recoñeceu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Cancelouse a autenticación"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Non se estableceu ningún PIN, padrón ou contrasinal"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Produciuse un erro ao realizar a autenticación"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar credencial do dispositivo"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Para continuar, mete a credencial do dispositivo"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Detectouse unha impresión dixital parcial. Téntao de novo."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"O sensor de impresión dixital está sucio. Límpao e téntao de novo."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar impresión dixital ou credencial do dispositivo"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utiliza a túa impresión dixital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Este dispositivo non admite o desbloqueo facial."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Desactivouse o sensor temporalmente."</string>
<string name="face_name_template" msgid="3877037340223318119">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Utilizar desbloqueo facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utilizar desbloqueo facial ou credencial do dispositivo"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Para continuar, utiliza o desbloqueo facial"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Icona cara"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Noticias e revistas"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapas e navegación"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produtividade"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accesibilidade"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Almacenamento do dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuración por USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index fd3a06baa3fa..334ba2d8b532 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"એપને તમારો ફોટો સંગ્રહ સંશોધિત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"આપના મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવા"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"એપને તમારા મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવાની મંજૂરી આપે છે."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"બાયોમેટ્રિક્સનો ઉપયોગ કરો"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"બાયોમેટ્રિક્સ અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"તે તમે જ છો એ ચકાસો"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"આગળ વધવા માટે બાયોમેટ્રિકનો ઉપયોગ કરો"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"પ્રમાણીકરણ રદ કર્યું"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ઓળખાયેલ નથી"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"પ્રમાણીકરણ રદ કર્યું"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"કોઈ પિન, પૅટર્ન અથવા પાસવર્ડ સેટ કરેલો નથી"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"પ્રમાણિત કરવામાં ભૂલ આવી"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ચાલુ રાખવા માટે તમારા ડિવાઇસની લૉગ ઇન વિગત દાખલ કરો"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"આંશિક ફિંગરપ્રિન્ટ મળી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ફિંગરપ્રિન્ટ પ્રક્રિયા કરી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ફિંગરપ્રિન્ટ સેન્સર ગંદું છે. કૃપા કરીને સાફ કરો અને ફરી પ્રયાસ કરો."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ફિંગરપ્રિન્ટ અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ચાલુ રાખવા માટે તમારી ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"આ ડિવાઇસ પર ફેસ અનલૉક કરવાની સુવિધા નથી."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string>
<string name="face_name_template" msgid="3877037340223318119">"ચહેરાનું <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ફેસ અનલૉકનો ઉપયોગ કરો"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ફેસ લૉક અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"આગળ વધવા માટે ફેસ અનલૉકનો ઉપયોગ કરો"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ચહેરા આઇકન"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"સમાચાર અને સામાયિકો"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps અને નેવિગેશન"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ઉત્પાદકતા"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ઍક્સેસિબિલિટી"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ડિવાઇસ સ્ટૉરેજ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ડિબગિંગ"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"કલાક"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index be64e904b908..16e848f83d6d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"इससे ऐप्लिकेशन को आपके फ़ोटो संग्रह में बदलाव करने की मंज़ूरी दी जाती है."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"अपने मीडिया संग्रह से जगह की जानकारी ऐक्सेस करने की अनुमति दें"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"इससे ऐप्लिकेशन को आपके मीडिया संग्रह से जगह की जानकारी ऐक्सेस करने की अनुमति दी जाती है."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"बायोमेट्रिक्स इस्तेमाल करें"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"बायोमेट्रिक्स या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"अपनी पहचान की पुष्टि करें"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"जारी रखने के लिए, बायोमेट्रिक्स इस्तेमाल करें"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध नहीं है"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द किया गया"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"पहचान नहीं हो पाई"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"प्रमाणीकरण रद्द किया गया"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"पिन, पैटर्न या पासवर्ड सेट नहीं है"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"गड़बड़ी की पुष्टि की जा रही है"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"जारी रखने के लिए, अपने डिवाइस का क्रेडेंशियल डालें"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"अधूरा फ़िंगरप्रिंट प्रोसेस हो सका. कृपया फिर से कोशिश करें."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फ़िंगरप्रिंट प्रोसेस नहीं हो सका. कृपया दोबारा कोशिश करें."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"फ़िंगरप्रिंट सेंसर गंदा है. कृपया साफ़ करें और फिर कोशिश करें."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फ़िंगरप्रिंट या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"जारी रखने के लिए फ़िंगरप्रिंट का इस्तेमाल करें"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"इस डिवाइस पर \'मालिक का चेहरा पहचानकर अनलॉक\' काम नहीं करती है."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
<string name="face_name_template" msgid="3877037340223318119">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"\'फ़ेस अनलॉक\' इस्तेमाल करें"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"चेहरा पहचानने की सुविधा या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"जारी रखने के लिए, \'फ़ेस अनलॉक\' इस्तेमाल करें"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"चेहरे का आइकॉन"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"समाचार और पत्रिकाएं"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps और नेविगेशन ऐप्लिकेशन"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"उत्पादकता"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"सुलभता"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"डिवाइस में जगह"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB डीबग करना"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"घंटा"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 3a6ce2179c96..15c36c9ba604 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -553,23 +553,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Omogućuje aplikaciji izmjenu vaše zbirke fotografija."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"čitanje lokacija iz vaše medijske zbirke"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Omogućuje aplikaciji čitanje lokacija iz vaše medijske zbirke."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Upotreba biometrije"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Upotreba biometrijske autentifikacije ili zaključavanja zaslona"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite da ste to vi"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Upotrijebite svoju biometrijsku autentifikaciju da biste nastavili"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikacija otkazana"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikacija otkazana"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nisu postavljeni PIN, uzorak ni zaporka"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Pogreška prilikom autentifikacije"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Upotreba zaključavanja zaslona"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Unesite vjerodajnicu uređaja da biste nastavili"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Otkriven je djelomični otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Senzor otiska prsta nije čist. Očistite ga i pokušajte ponovo."</string>
@@ -592,10 +587,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Upotreba otiska prsta ili zaključavanja zaslona"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -641,12 +634,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Otključavanje licem nije podržano na ovom uređaju."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je privremeno onemogućen."</string>
<string name="face_name_template" msgid="3877037340223318119">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Upotreba otključavanja licem"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Upotreba lica ili zaključavanja zaslona"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Upotrijebite otključavanje licem da biste nastavili"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona lica"</string>
@@ -1998,8 +1988,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Vijesti i časopisi"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Karte i navigacija"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivnost"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Pristupačnost"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Pohrana na uređaju"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Otklanjanje pogrešaka putem USB-a"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"sat"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3cd0aba28c04..f28f1b66518f 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Engedélyezi az alkalmazásnak a fényképgyűjtemény módosítását."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"helyek olvasása a médiagyűjteményből"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Engedélyezi az alkalmazásnak a helyek médiagyűjteményből való olvasását."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biometriai feloldás használata"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"A folytatás biometriai feloldással vagy képernyőzárral lehetséges"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Igazolja, hogy Ön az"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"A folytatás biometriai feloldással lehetséges"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrikus hardver nem áll rendelkezésre"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Hitelesítés megszakítva"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nem ismerhető fel"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Hitelesítés megszakítva"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nem állított be PIN-kódot, mintát vagy jelszót."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Hiba történt a hitelesítés közben"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Képernyőzár használata"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"A folytatáshoz adja meg az eszköz hitelesítési adatait"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"A rendszer az ujjlenyomatnak csak egy részletét érzékelte. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Az ujjlenyomat-olvasó koszos. Tisztítsa meg, majd próbálkozzon újra."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"A folytatás ujjlenyomattal vagy képernyőzárral lehetséges"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"A folytatáshoz használja ujjlenyomatát"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Az eszköz nem támogatja az arcalapú feloldást"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Az érzékelő átmenetileg le van tiltva."</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> arc"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Arcalapú feloldás használata"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"A folytatás arcalapú feloldással vagy képernyőzárral lehetséges"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"A folytatás arcalapú feloldással lehetséges"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Arcikon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Hírlapok és folyóiratok"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Térképek és navigáció"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Irodai alkalmazások"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Kisegítő alkalmazások"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Eszköztárhely"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-hibakeresés"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"óra"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 8d6e42e1e7ff..959cb572333e 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Թույլ է տալիս հավելվածին փոփոխել ձեր լուսանկարների հավաքածուն:"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ճանաչել տեղադրության մասին տվյալները մեդիա բովանդակության հավաքածուից"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Թույլ է տալիս հավելվածին ճանաչել տեղադրության մասին տվյալները ձեր մեդիա բովանդակության հավաքածուից:"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Կենսաչափական համակարգեր"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Օգտագործել կենսաչափական համակարգեր կամ էկրանի կողպում"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Հաստատեք ձեր ինքնությունը"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Շարունակելու համար օգտագործեք կենսաչափական համակարգեր"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Կենսաչափական սարքը հասանելի չէ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Նույնականացումը չեղարկվեց"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Չհաջողվեց ճանաչել"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Նույնականացումը չեղարկվեց"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ավելացրեք PIN կոդ, նախշ կամ գաղտնաբառ։"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Չհաջողվեց նույնականացնել"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Էկրանի կողպում"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Շարունակելու համար մուտքագրեք սարքի նույնականացման տվյալները"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Մատնահետքն ամբողջությամբ չի սկանավորվել: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Օգտագործել մատնահետք"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Օգտագործել մատնահետք կամ էկրանի կողպում"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Շարունակելու համար անհրաժեշտ է ձեր մատնահետքը"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Դեմքով ապակողպումն այս սարքում չի աջակցվում"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Տվիչը ժամանակավորապես անջատված է:"</string>
<string name="face_name_template" msgid="3877037340223318119">"Դեմք <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Օգտագործել դեմքով ապակողպում"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Օգտագործել դեմքով ապակողպում կամ էկրանի կողպում"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Շարունակելու համար օգտագործեք դեմքով ապակողպում"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Դեմքի պատկերակ"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Նորություններ և ամսագրեր"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Քարտեզներ և նավարկում"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Արդյունավետություն"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Հատուկ գործառույթներ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Սարքի հիշողություն"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-ով վրիպազերծում"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ժամ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 28d2ed8963b0..9f692098f12a 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Mengizinkan aplikasi untuk memodifikasi koleksi foto Anda."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"membaca lokasi dari koleksi media Anda"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Mengizinkan aplikasi untuk membaca lokasi dari koleksi media Anda."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Gunakan biometrik"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gunakan biometrik atau kunci layar"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifikasi bahwa ini memang Anda"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gunakan biometrik untuk melanjutkan"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrik tidak tersedia"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentikasi dibatalkan"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tidak dikenali"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentikasi dibatalkan"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Tidak ada PIN, pola, atau sandi yang disetel"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error saat mengautentikasi"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gunakan kunci layar"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Masukkan kredensial perangkat untuk melanjutkan"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Sebagian sidik jari terdeteksi. Coba lagi."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Tidak dapat memproses sidik jari. Coba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Sensor sidik jari kotor. Bersihkan dan coba lagi."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan sidik jari atau kunci layar"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gunakan sidik jari untuk melanjutkan"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Face unlock tidak didukung di perangkat ini."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor dinonaktifkan untuk sementara."</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> wajah"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gunakan face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gunakan face lock atau kunci layar"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Gunakan face unlock untuk melanjutkan"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikon wajah"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Berita &amp; Majalah"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Peta &amp; Navigasi"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivitas"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Aksesibilitas"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Penyimpanan perangkat"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Proses debug USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"jam"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index cd5b35affb86..0e11f033b033 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Leyfir forritinu að breyta myndasafninu þínu."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lesa staðsetningar úr efnissafninu þínu"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Leyfir forritinu að lesa staðsetningar úr efnissafninu þínu."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Nota lífkenni"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Nota lífkenni eða skjálás"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Staðfestu hver þú ert"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Notaðu lífkenni til að halda áfram"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Lífkennavélbúnaður ekki tiltækur"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Hætt við auðkenningu"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Þekktist ekki"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Hætt við auðkenningu"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ekkert PIN-númer, mynstur eða aðgangsorð stillt"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Villa við auðkenningu"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Nota skjálás"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Færðu inn skilríki tækisins til að halda áfram"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Hluti fingrafars greindist. Reyndu aftur."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingrafaraskynjarinn er óhreinn. Hreinsaðu hann og reyndu aftur."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Nota fingrafar eða skjálás"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Notaðu fingrafarið þitt til að halda áfram"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Andlitsopnun er ekki studd í þessu tæki."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Slökkt tímabundið á skynjara."</string>
<string name="face_name_template" msgid="3877037340223318119">"Andlit <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Nota andlitsopnun"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Nota andlit eða skjálás"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Notaðu andlitsopnun til að halda áfram"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Andlitstákn"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Fréttir og tímarit"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kort og leiðsögn"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Aðstoð"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Aðgengi"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Geymslurými tækis"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-villuleit"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"klst."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 83b4a684ae58..b41996e4cb58 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Consente all\'app di modificare la tua raccolta di foto."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lettura delle posizioni dalla tua raccolta multimediale"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Consente all\'app di leggere le posizioni dalla tua raccolta multimediale."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Usa la biometria"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usa la biometria o il blocco schermo"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica la tua identità"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Usa la biometria per continuare"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrico non disponibile"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticazione annullata"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Non riconosciuto"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticazione annullata"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Non hai impostato PIN, sequenza o password"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Errore durante l\'autenticazione"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usa il blocco schermo"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Inserisci la credenziale del dispositivo per continuare"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Rilevata impronta parziale. Riprova."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossibile elaborare l\'impronta. Riprova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Il sensore di impronte è sporco. Puliscilo e riprova."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usa l\'impronta o il blocco schermo"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilizza la tua impronta per continuare"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Sblocco con il volto non supportato su questo dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensore temporaneamente disattivato."</string>
<string name="face_name_template" msgid="3877037340223318119">"Volto <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usa Sblocco con il volto"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usa Sblocco con il volto o il blocco schermo"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Usa Sblocco con il volto per continuare"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Icona volto"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Notizie e riviste"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps e Navigatore"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produttività"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibilità"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Memoria dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Debug USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ora"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index e3cb913fe0fd..be05cc00c3b0 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"מאפשרת לאפליקציה לשנות את אוסף התמונות שלך."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"לקרוא מיקומים מאוסף המדיה שלך"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"מאפשרת לאפליקציה לקרוא מיקומים מאוסף המדיה שלך."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"שימוש במידע ביומטרי"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"שימוש במידע ביומטרי בנעילת מסך"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"אימות זהותך"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"יש להשתמש במידע ביומטרי כדי להמשיך"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"חומרה ביומטרית לא זמינה"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"האימות בוטל"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"לא זוהתה"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"האימות בוטל"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"עוד לא הוגדרו קוד גישה, קו ביטול נעילה או סיסמה"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"שגיאה באימות"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"שימוש בנעילת מסך"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"יש להזין את פרטי הכניסה של המכשיר כדי להמשיך"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"זוהתה טביעת אצבע חלקית. אפשר לנסות שוב."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"החיישן של טביעות האצבעות מלוכלך. צריך לנקות אותו ולנסות שוב."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר זה אין חיישן טביעות אצבע."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"שימוש בטביעת אצבע"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"שימוש בטביעת אצבע או בנעילת מסך"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"יש להשתמש בטביעת האצבע כדי להמשיך"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"המכשיר הזה לא תומך בשחרור נעילה על ידי זיהוי פנים."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"החיישן מושבת באופן זמני."</string>
<string name="face_name_template" msgid="3877037340223318119">"פנים <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"שחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"שימוש בזיהוי פנים או בנעילת מסך"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"יש להשתמש בשחרור נעילה על ידי זיהוי פנים כדי להמשיך"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"סמל הפנים"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"חדשות וכתבי עת"</string>
<string name="app_category_maps" msgid="6395725487922533156">"מפות וניווט"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"פרודוקטיביות"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"נגישות"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"שטח האחסון במכשיר"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"‏ניקוי באגים ב-USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"שעה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4b54bdbc2469..e7c343872b95 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"写真コレクションの変更をアプリに許可します。"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"メディア コレクションの位置情報の読み取り"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"メディア コレクションの位置情報の読み取りをアプリに許可します。"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"生体認証の使用"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"生体認証または画面ロックの使用"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"本人確認"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"続行するには生体認証を使用してください"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"生体認証ハードウェアが利用できません"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"認証をキャンセルしました"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"認識されませんでした"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"認証をキャンセルしました"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN、パターン、パスワードが設定されていません"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"エラー認証"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"画面ロックの使用"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"続行するにはデバイスの認証情報を入力してください"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"指紋を処理できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"指紋認証センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"指紋の使用"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"指紋または画面ロックの使用"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"続行するには指紋認証を使用してください"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"このデバイスでは、顔認証はご利用いただけません。"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"センサーが一時的に無効になっています。"</string>
<string name="face_name_template" msgid="3877037340223318119">"顔 <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"顔認証の使用"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"顔認証または画面ロックの使用"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"続行するには顔認証を使用してください"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"顔アイコン"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ニュース&雑誌"</string>
<string name="app_category_maps" msgid="6395725487922533156">"地図&ナビ"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"仕事効率化"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ユーザー補助"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"デバイスのストレージ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB デバッグ"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"時"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 7a03bb32efcb..3fb6c6954a98 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"აპი შეძლებს თქვენი ფოტოკოლექციის შეცვლას."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"მდებარეობების გაცნობა თქვენი მედიაკოლექციიდან"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"აპი შეძლებს მდებარეობების გაცნობას თქვენი მედიაკოლექციიდან."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"გამოიყენეთ ბიომეტრიული სისტემა"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"გამოიყენეთ ბიომეტრიული სისტემა ან ეკრანის დაბლოკვა"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"დაადასტურეთ ვინაობა"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"გასაგრძელებლად გამოიყენეთ თქვენი ბიომეტრიული მონაცემები"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ბიომეტრიული აპარატურა მიუწვდომელია"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ავტორიზაცია გაუქმდა"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"არ არის ამოცნობილი"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ავტორიზაცია გაუქმდა"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-კოდი, ნიმუში ან პაროლი დაყენებული არ არის"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"შეცდომა ავთენტიკაციისას"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"გამოიყენეთ ეკრანის დაბლოკვა"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"გასაგრძელებლად შეიყვანეთ თქვენი მოწყობილობის ავტორიზაციის მონაცემი"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"თითის ანაბეჭდის დამუშავება ვერ მოხერხდა. გთხოვთ, ცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"თითის ანაბეჭდის სენსორი დაბინძურებულია. გთხოვთ, გაასუფთაოთ და სცადოთ ხელახლა."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"გამოიყენეთ თითის ანაბეჭდი"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"გამოიყენეთ თითის ანაბეჭდი ან ეკრანის დაბლოკვა"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"გასაგრძელებლად გამოიყენეთ თქვენი თითის ანაბეჭდი"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"განბლოკვა სახით ამ მოწყობილობაზე მხარდაჭერილი არ არის."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"სენსორი დროებით გათიშულია."</string>
<string name="face_name_template" msgid="3877037340223318119">"სახე <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"გამოიყენეთ სახით განბლოკვა"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"გამოიყენეთ სახე ან ეკრანის დაბლოკვა"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"გასაგრძელებლად გამოიყენეთ სახით განბლოკვა"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"სახის ხატულა"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ახალი ამბები და ჟურნალები"</string>
<string name="app_category_maps" msgid="6395725487922533156">"რუკები და ნავიგაცია"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"პროდუქტიულობა"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"მარტივი წვდომა"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"მოწყობილობის მეხსიერება"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB გამართვა"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"საათი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1c00fe65ba82..4404603f09fc 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Қолданбаға суреттер жинағын өзгертуге мүмкіндік береді."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"медиамазмұн жинағынан геодеректерді оқу"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Қолданбаға медиамазмұн жинағынан геодеректерді оқуға мүмкіндік береді."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Биометриканы пайдалану"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометриканы немесе экран құлпын пайдалану"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Бұл сіз екеніңізді растаңыз"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Жалғастыру үшін биометрикаңызды пайдаланыңыз."</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалық жабдық жоқ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аутентификациядан бас тартылды."</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Танылмады"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аутентификациядан бас тартылды."</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ешқандай PIN коды, өрнек немесе құпия сөз орнатылмаған."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Аутентификациялауда қате шықты."</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Экран құлпын пайдалану"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Жалғастыру үшін құрылғының тіркелу деректерін енгізіңіз."</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Саусақ ізі толық анықталмады. Әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сканер лас. Тазалап, әрекетті қайталаңыз."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> саусағы"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Саусақ ізін пайдалану"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Саусақ ізін немесе экран құлпын пайдалану"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Жалғастыру үшін саусақ ізін пайдаланыңыз."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Бұл құрылғыда Face Unlock функциясы істемейді."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Датчик уақытша өшірулі."</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> беті"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Face Unlock функциясын пайдалану"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Face Lock функциясын немесе экран құлпын пайдалану"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Жалғастыру үшін Face Unlock функциясын пайдаланыңыз."</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Бет белгішесі"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Газеттер және журналдар"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карта және навигация"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Өнімділік"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Арнайы мүмкіндіктер"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Құрылғы жады"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB арқылы түзету"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"сағат"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a2512d2a9d08..fb1155318137 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"អនុញ្ញាតឱ្យ​កម្មវិធី​កែប្រែ​បណ្ដុំ​រូបថត​របស់​អ្នក។"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"អាន​ទីតាំង​ពី​បណ្ដុំ​មេឌៀ​របស់​អ្នក"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"អនុញ្ញាតឱ្យ​កម្មវិធី​អាន​ទីតាំង​ពីបណ្ដុំ​មេឌៀ​របស់​អ្នក។"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ប្រើ​ជីវមាត្រ"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ប្រើជីវមាត្រ ឬ​ការចាក់សោអេក្រង់"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ផ្ទៀងផ្ទាត់ថាជាអ្នក"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ប្រើជីវមាត្រ​របស់អ្នក ដើម្បីបន្ត"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"មិនអាច​ប្រើឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ​បានទេ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"បាន​បោះបង់​ការ​ផ្ទៀងផ្ទាត់"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"មិនអាចសម្គាល់បានទេ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"បាន​បោះបង់​ការ​ផ្ទៀងផ្ទាត់"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"គ្មាន​ការកំណត់​កូដ pin លំនាំ ឬពាក្យសម្ងាត់​ទេ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"មានបញ្ហាក្នុង​ការផ្ទៀងផ្ទាត់"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ប្រើ​ការ​ចាក់​សោ​អេក្រង់"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"បញ្ចូល​ព័ត៌មានផ្ទៀងផ្ទាត់​ឧបករណ៍​របស់អ្នក ដើម្បីបន្ត"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ឧបករណ៍ចាប់ស្នាមម្រាមដៃគឺប្រឡាក់។ សូមសម្អាត រួចព្យាយាមម្តងទៀត។"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ។"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទ​ឧបករណ៍​ចាប់សញ្ញាជា​បណ្តោះអាសន្ន។"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃ <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ប្រើស្នាមម្រាមដៃ"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ប្រើស្នាមម្រាមដៃ ឬ​ការចាក់សោអេក្រង់"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ប្រើ​ស្នាមម្រាមដៃ​របស់អ្នក ដើម្បីបន្ត"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"មិនអាចប្រើ​ការដោះសោតាមទម្រង់មុខ​នៅលើ​ឧបករណ៍​នេះ​បានទេ។"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"បានបិទ​ឧបករណ៍​ចាប់សញ្ញាជា​បណ្តោះអាសន្ន។"</string>
<string name="face_name_template" msgid="3877037340223318119">"ផ្ទៃមុខទី <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ប្រើការដោះសោ​តាមទម្រង់មុខ"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ប្រើមុខ ឬ​ការចាក់សោអេក្រង់"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ប្រើការដោះសោ​តាមទម្រង់មុខ ដើម្បីបន្ត"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"រូប​ផ្ទៃមុខ"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ព័ត៌មាន និង​ទស្សនាវដ្ដី"</string>
<string name="app_category_maps" msgid="6395725487922533156">"ផែនទី និង​ការ​រុករក"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ផលិត​ភាព"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ភាពងាយស្រួល"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ទំហំផ្ទុកឧបករណ៍"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"ការ​កែកំហុសតាម USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ម៉ោង"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index e9110c217d4a..f484344d7891 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"ನಿಮ್ಮ ಫೋಟೋ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಿ"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ಬಯೋಮೆಟ್ರಿಕ್ಸ್ ಬಳಸಿ"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ಬಯೋಮೆಟ್ರಿಕ್ಸ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿ"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ಇದು ನೀವೇ ಎಂದು ಪರಿಶೀಲಿಸಿ"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಬಯೋಮೆಟ್ರಿಕ್ ಬಳಸಿ"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ಪಿನ್, ಪ್ಯಾಟರ್ನ್ ಅಥವಾ ಪಾಸ್‌ವರ್ಡ್ ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ದೃಢೀಕರಿಸುವಾಗ ದೋಷ ಎದುರಾಗಿದೆ"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಸಾಧನದ ರುಜುವಾತನ್ನು‌ ನಮೂದಿಸಿ"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ಭಾಗಶಃ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಕೊಳೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"ಈ ಸಾಧನದಲ್ಲಿ ಫೇಸ್ ಅನ್‌ಲಾಕ್ ವೈಶಿಷ್ಟ್ಯವು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="face_name_template" msgid="3877037340223318119">"ಮುಖದ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಬಳಸಿ‌‌"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ಫೇಸ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿ"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ಮುಂದುವರಿಸಲು ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಬಳಸಿ"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ಮುಖದ ಐಕಾನ್‌"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ಸುದ್ದಿ ಮತ್ತು ನಿಯತಕಾಲಿಕೆಗಳು"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps ಮತ್ತು ನ್ಯಾವಿಗೇಶನ್"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ಉತ್ಪಾದಕತೆ"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ಪ್ರವೇಶಿಸುವಿಕೆ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ಸಾಧನ ಸಂಗ್ರಹಣೆ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ಗಂಟೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 16e2efa6f5c0..78cc1b572e04 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"앱에서 사진 컬렉션을 수정하도록 허용합니다."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"미디어 컬렉션에서 위치 읽기"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"앱에서 미디어 컬렉션의 위치를 읽도록 허용합니다."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"생체 인식 사용"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"생체 인식 또는 화면 잠금을 사용"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"본인 확인"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"생체 인식을 사용하여 계속하세요"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"생체 인식 하드웨어를 사용할 수 없음"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"인증이 취소되었습니다."</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"인식할 수 없음"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"인증이 취소되었습니다."</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN, 패턴, 비밀번호가 설정되지 않음"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"인증 오류"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"화면 잠금 사용"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"계속하려면 기기의 사용자 인증 정보를 입력하세요"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"지문이 일부만 인식되었습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"지문 센서를 깨끗이 닦고 다시 시도하세요."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"지문 사용"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"지문 또는 화면 잠금 사용"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"계속하려면 지문을 사용하세요."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"이 기기에서는 얼굴인식 잠금해제가 지원되지 않습니다."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"센서가 일시적으로 사용 중지되었습니다."</string>
<string name="face_name_template" msgid="3877037340223318119">"얼굴 <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"얼굴인식 잠금해제 사용"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"얼굴 또는 화면 잠금 사용"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"얼굴인식 잠금해제를 사용하여 계속하세요"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"얼굴 아이콘"</string>
@@ -1699,7 +1689,7 @@
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"기능 간에 전환하려면 세 손가락을 사용하여 위로 스와이프한 다음 잠시 기다립니다."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"확대"</string>
<string name="user_switched" msgid="7249833311585228097">"현재 사용자는 <xliff:g id="NAME">%1$s</xliff:g>님입니다."</string>
- <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 전환하는 중…"</string>
+ <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>로 전환하는 중…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g>님을 로그아웃하는 중…"</string>
<string name="owner_name" msgid="8713560351570795743">"소유자"</string>
<string name="error_message_title" msgid="4082495589294631966">"오류"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"뉴스/잡지"</string>
<string name="app_category_maps" msgid="6395725487922533156">"지도/내비게이션"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"생산성"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"접근성"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"기기 저장용량"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB 디버깅"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"시"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 258422574dd7..5d8b8980d2f5 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа жыйнагыңыз сакталган жерлерди окуу"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Биометрикалык жөндөөлөрдү колдонуу"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометрикалык жөндөөнү же экрандын кулпусун колдонуу"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Улантуу үчүн биометрикалык жөндөөнү колдонуу"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аныктыгын текшерүү жокко чыгарылды"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN код, графикалык ачкыч же сырсөз коюлган жок"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Аутентификация катасы"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Экран кулпусун колдонуу"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Улантуу үчүн түзмөгүңүздүн эсептик дайындарын киргизиңиз"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Манжа изи жарым-жартылай аныкталды. Кайталап көрүңүз."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Манжа изи иштелбей койду. Кайталап көрүңүз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Манжа изинин сенсору кирдеп калган. Тазалап, кайталап көрүңүз."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Манжа изин колдонуу"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Манжа изин же экрандын кулпусун колдонуу"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Улантуу үчүн манжаңыздын изин колдонуңуз"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Жүзүнөн таануу функциясы бул түзмөктө иштебейт."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Сенсор убактылуу өчүрүлгөн."</string>
<string name="face_name_template" msgid="3877037340223318119">"Жүз <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Жүзүнөн таанып ачууну колдонуу"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Жүзүнөн таанып ачууну же экрандын кулпусун колдонуу"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Улантуу үчүн жүзүнөн таанып ачууну колдонуу"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Жүздүн сүрөтчөсү"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Жаңылыктар жана журналдар"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карталар жана чабыттоо"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Өндүрүш категориясы"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Атайын мүмкүнчүлүктөр"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Түзмөктүн сактагычы"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB аркылуу мүчүлүштүктөрдү аныктоо"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"саат"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7fb32e7d2034..fcf47edbfc47 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"ອະນຸຍາດໃຫ້ແອັບແກ້ໄຂຄໍເລັກຊັນຮູບຂອງທ່ານ."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"ອະນຸຍາດໃຫ້ແອັບອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ໃຊ້ລະບົບຊີວະມິຕິ"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ໃຊ້ລະບົບຊີວະມິຕິ ຫຼື ການລັອກໜ້າຈໍ"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ຢັ້ງຢືນວ່າແມ່ນທ່ານ"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ໃຊ້ລະບົບຊີວະມິຕິຂອງທ່ານເພື່ອດຳເນີນການຕໍ່"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ຮາດແວຊີວະມິຕິບໍ່ສາມາດໃຊ້ໄດ້"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ຍົກເລີກການຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ບໍ່ຮັບຮູ້"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ຍົກເລີກການຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ບໍ່ໄດ້ຕັ້ງ PIN, ຮູບແບບປົດລັອກ ຫຼື ລະຫັດຜ່ານ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ເກີດຄວາມຜິດພາດໃນການພິສູດຢືນຢັນ"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ໃຊ້ການລັອກໜ້າຈໍ"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ລະບຸຂໍ້ມູນການເຂົ້າສູ່ລະບົບອຸປະກອນຂອງທ່ານເພື່ອດຳເນີນການຕໍ່"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ກວດ​ພົບ​ລາຍ​ນີ້ວ​ມື​ບາງ​ສ່ວນ​ແລ້ວ. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ບໍ່​ສາ​ມາດ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ໄດ້. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ເຊັນ​ເຊີ​ລາຍ​ນີ້ວ​ມື​ເປື້ອນ. ກະ​ລຸ​ນາ​ທຳ​ຄວາມ​ສະ​ອາດ ແລະ​ລອງ​ໃໝ່​ອີກ."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວ​ມື <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ໃຊ້ລາຍນິ້ວມື"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ໃຊ້ລາຍນິ້ວມື ຫຼື ການລັອກໜ້າຈໍ"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ໃຊ້​ລາຍ​ນີ້ວ​ມື​ຂອງ​ທ່ານ​ເພື່ອ​ສືບ​ຕໍ່"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"ບໍ່ຮອງຮັບການປົດລັອກດ້ວຍໜ້າຢູ່ອຸປະກອນນີ້."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string>
<string name="face_name_template" msgid="3877037340223318119">"ໃບໜ້າ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ໃຊ້ການປົດລັອກດ້ວຍໜ້າ"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ໃຊ້ໃບໜ້າ ຫຼື ການລັອກໜ້າຈໍ"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ໃຊ້ການປົດລັອກດ້ວຍໜ້າເພື່ອດຳເນີນການຕໍ່"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ໄອຄອນໃບໜ້າ"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"News &amp; Magazines"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ຜະລິດຕະພາບ"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ບ່ອນຈັດເກັບຂໍ້ມູນອຸປະກອນ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"ການດີບັກຜ່ານ USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ຊົ່ວໂມງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 820d7a029b9e..4afef3be17d4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Programai leidžiama keisti nuotraukų kolekciją."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"skaityti vietoves iš medijos kolekcijos"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Programai leidžiama skaityti vietoves iš medijos kolekcijos."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Naudoti biometrinius duomenis"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Naudoti biometrinius duomenis arba ekrano užraktą"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Patvirtinkite, kad tai jūs"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Norėdami tęsti, naudokite biometrinius duomenis"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrinė aparatinė įranga nepasiekiama"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikavimas atšauktas"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Neatpažinta"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikavimas atšauktas"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenustatytas PIN kodas, atrakinimo piešinys arba slaptažodis"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Autentifikuojant įvyko klaida"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Naudoti ekrano užraktą"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Jei norite tęsti, įveskite įrenginio prisijungimo duomenis"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Aptiktas dalinis piršto antspaudas. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nepavyko apdoroti piršto antspaudo. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Piršto antspaudo jutiklis purvinas. Nuvalykite ir bandykite dar kartą."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Naudoti kontrolinį kodą arba ekrano užraktą"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Naudokite kontrolinį kodą, kad galėtumėte tęsti"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Atrakinimas pagal veidą šiame įrenginyje nepalaikomas."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Jutiklis laikinai išjungtas."</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g> veidas"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Naudoti atrakinimą pagal veidą"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Naudoti atrakinimą pagal veidą arba ekrano užraktą"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Norėdami tęsti, naudokite atrakinimą pagal veidą"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Veido pkt."</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Naujienos ir žurnalai"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Žemėlapiai ir navigacija"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktyvumas"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Pritaikomumas"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Įrenginio saugykla"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB derinimas"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"valanda"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 91c38a4ac3bd..8fbfb279daf9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -553,23 +553,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Ļauj lietotnei pārveidot jūsu fotoattēlu kolekciju."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"Lasīt atrašanās vietas no jūsu multivides kolekcijas"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Ļauj lietotnei lasīt atrašanās vietas no jūsu multivides kolekcijas."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biometrijas izmantošana"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrijas vai ekrāna bloķēšanas izmantošana"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Apstipriniet, ka tas esat jūs"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Lai turpinātu, izmantojiet biometriju"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisko datu aparatūra nav pieejama"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikācija ir atcelta"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Dati nav atpazīti"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikācija ir atcelta"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN, kombinācija vai parole nav iestatīta"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Autentifikācijas kļūda"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekrāna bloķēšanas metodes izmantošana"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Lai turpinātu, ievadiet savas ierīces akreditācijas datus"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Noteikts daļējs pirksta nospiedums. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Pirkstu nospiedumu sensors ir netīrs. Lūdzu, notīriet to un mēģiniet vēlreiz."</string>
@@ -592,10 +587,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Pirksta nospieduma vai ekrāna bloķēšanas metodes izmantošana"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Lai turpinātu, izmantojiet pirksta nospiedumu"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -641,12 +634,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Autorizācija pēc sejas šajā ierīcē netiek atbalstīta"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensors ir īslaicīgi atspējots."</string>
<string name="face_name_template" msgid="3877037340223318119">"Seja <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Autorizācija pēc sejas"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Autorizācijas pēc sejas vai ekrāna bloķēšanas metodes izmantošana"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Lai turpinātu, izmantojiet autorizāciju pēc sejas"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Sejas ikona"</string>
@@ -1998,8 +1988,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Ziņas un žurnāli"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kartes un navigācija"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivitāte"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Pieejamība"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Ierīces krātuve"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB atkļūdošana"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"stunda"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 88fb8246845f..9d07234910fe 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="byteShort" msgid="202579285008794431">"Б"</string>
+ <string name="byteShort" msgid="202579285008794431">"B"</string>
<string name="kilobyteShort" msgid="2214285521564195803">"KB"</string>
<string name="megabyteShort" msgid="6649361267635823443">"MB"</string>
<string name="gigabyteShort" msgid="7515809460261287991">"GB"</string>
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Дозволува апликацијата да ја менува вашата збирка на фотографии."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"да чита локации од вашата збирка на аудиовизуелни содржини"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Дозволува апликацијата да чита локации од вашата збирка на аудиовизуелни содржини."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Користи биометрика"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Користи биометрика или заклучен екран"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдете дека сте вие"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Користете ја вашата биометрика за да продолжите"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрискиот хардвер е недостапен"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Проверката е откажана"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Непознат"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Проверката е откажана"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не е поставен PIN, шема или лозинка"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при проверката"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Користи заклучување екран"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Внесете го акредитивот на уредот за да продолжите"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Откриен е делумен отпечаток. Обидете се повторно."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатокот не може да се обработи. Обидете се повторно."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користи отпечаток"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користи отпечаток или заклучување екран"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Користете го отпечатокот за да продолжите"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"„Отклучувањето со лик“ не е поддржано на уредов."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Сензорот е привремено оневозможен."</string>
<string name="face_name_template" msgid="3877037340223318119">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Користи отклучување со лик"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Користи лик или заклучување екран"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Користете отклучување со лик за да продолжите"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Икона"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Вести и списанија"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карти и навигација"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Продуктивност"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Пристапност"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Простор на уредот"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отстранување грешки на USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"час"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index f82267d115eb..8d91c5182b16 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"നിങ്ങളുടെ ഫോട്ടോ ശേഖരം പരിഷ്‌ക്കരിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുക"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ബയോമെട്രിക്‌സ് ഉപയോഗിക്കുക"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ബയോമെട്രിക്‌സ് അല്ലെങ്കിൽ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ഇത് നിങ്ങളാണെന്ന് പരിശോധിച്ചുറപ്പിക്കുക"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"തുടരാൻ ബയോമെട്രിക് ഉപയോഗിക്കുക"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ബയോമെട്രിക് ഹാർ‌ഡ്‌വെയർ ലഭ്യമല്ല"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"തിരിച്ചറിഞ്ഞില്ല"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"പിന്നോ പാറ്റേണോ പാസ്‌വേഡോ സജ്ജീകരിച്ചിട്ടില്ല"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"പിശക് പരിശോധിച്ചുറപ്പിക്കുന്നു"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"തുടരാൻ നിങ്ങളുടെ ഉപകരണ ക്രെഡൻഷ്യലുകൾ നൽകുക"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ഫിംഗർപ്രിന്റ് ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെളിയുണ്ട്. അത് വൃത്തിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"കൈവിരൽ <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ഫിംഗർപ്രിന്റ് അല്ലെങ്കിൽ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"തുടരുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
<string name="face_name_template" msgid="3877037340223318119">"മുഖം <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഉപയോഗിക്കുക"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"മുഖം അല്ലെങ്കിൽ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"തുടരാൻ മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഉപയോഗിക്കുക"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"മുഖത്തിന്റെ ഐക്കൺ"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"വാർത്തകളും മാസികകളും"</string>
<string name="app_category_maps" msgid="6395725487922533156">"മാപ്പുകളും നാവിഗേഷനും"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ഉല്‍‌പ്പാദനക്ഷമത"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ഉപയോഗസഹായി"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ഉപകരണ സ്റ്റോറേജ്"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ഡീബഗ്ഗിംഗ്"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"മണി."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 203ad3d39060..faec2be635ba 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Таны зургийн цуглуулгыг тохируулах зөвшөөрлийг аппад олгодог."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа цуглуулгаасаа байршлыг унших"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Таны медиа цуглуулгаас байршлыг унших зөвшөөрлийг аппад олгодог."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Биометр ашиглах"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометр эсвэл дэлгэцийн түгжээ ашиглах"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Өөрийгөө мөн гэдгийг баталгаажуулаарай"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Үргэлжлүүлэхийн тулд биометрээ ашиглана уу"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрийн техник хангамж боломжгүй байна"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Нотолгоог цуцаллаа"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Таниагүй"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Нотолгоог цуцаллаа"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Тохируулсан пин, хээ эсвэл нууц үг алга"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Баталгаажуулахад алдаа гарлаа"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Дэлгэцийн түгжээг ашиглах"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Үргэлжлүүлэхийн тулд төхөөрөмжийнхөө мандат үнэмлэхийг оруулна уу"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Хурууны хээ мэдрэгч бохирдсон байна. Цэвэрлэсний дараа дахин оролдоно уу."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Хурууны хээ ашиглах"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Хурууны хээ эсвэл дэлгэцийн түгжээ ашиглах"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Үргэлжлүүлэхийн тулд хурууны хээгээ ашиглаарай"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Царайгаар тайлахыг энэ төхөөрөмж дээр дэмждэггүй."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
<string name="face_name_template" msgid="3877037340223318119">"Царай <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Царайгаар тайлахыг ашиглах"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Царай эсвэл дэлгэцийн түгжээ ашиглах"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Үргэлжлүүлэхийн тулд царайгаар тайлахыг ашиглана уу"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Царайны дүрс тэмдэг"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Мэдээ &amp; сэтгүүл"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Газрын зураг &amp; зүг чиг"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Бүтээмж"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Хандалт"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Төхөөрөмжийн сан"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB дебаг хийлт"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"Цаг"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index d8d43a64ab7b..3bf30638d95a 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Membenarkan apl mengubah suai koleksi foto anda."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"baca lokasi daripada koleksi media anda"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Membenarkan apl membaca lokasi daripada koleksi media anda."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Gunakan biometrik"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gunakan biometrik atau kunci skrin"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Sahkan itu anda"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gunakan biometrik anda untuk meneruskan"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Perkakasan biometrik tidak tersedia"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Pengesahan dibatalkan"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tidak dikenali"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Pengesahan dibatalkan"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Pin, corak atau kata laluan tidak ditetapkan"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Ralat semasa membuat pengesahan"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gunakan kunci skrin"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Masukkan bukti kelayakan peranti anda untuk meneruskan"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Cap jari separa dikesan. Sila cuba lagi."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Penderia cap jari kotor. Sila bersihkan dan cuba lagi."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan cap jari atau kunci skrin"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gunakan cap jari anda untuk teruskan"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Wajah buka kunci tidak disokong pada peranti ini."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Penderia dilumpuhkan sementara."</string>
<string name="face_name_template" msgid="3877037340223318119">"Wajah <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gunakan wajah buka kunci"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gunakan kunci wajah atau skrin"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Gunakan wajah buka kunci untuk meneruskan"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikon wajah"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Berita &amp; Majalah"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Peta &amp; Navigasi"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktiviti"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Kebolehaksesan"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Storan peranti"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Penyahpepijatan USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"jam"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 82008b069346..f301483c6b56 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"အက်ပ်အား သင့်ဓာတ်ပုံစုစည်းမှုကို ပြုပြင်ခွင့်ပေးသည်။"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခြင်း"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"အက်ပ်အား သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခွင့်ပေးသည်။"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ဇီဝမက်ထရစ်အချက်အလက်များ သုံးခြင်း"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ဇီဝမက်ထရစ်အချက်အလက်များ (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"သင်ဖြစ်ကြောင်း အတည်ပြုပါ"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ရှေ့ဆက်ရန် သင်၏ ဇီဝမက်ထရစ်အချက်အလက်ကို သုံးပါ"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"မသိ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ပင်နံပါတ်၊ လော့ခ်ပုံစံ သို့မဟုတ် စကားဝှက် သတ်မှတ်မထားပါ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"အထောက်အထားစိစစ်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ရှေ့ဆက်ရန် သင့်စက်ပစ္စည်း၏ အထောက်အထားကို ထည့်သွင်းပါ"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"လက်ဗွေ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"လက်ဗွေယူ၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"လက်ဗွေဖတ်ကိရိယာ ညစ်ပေနေသည်။ ကျေးဇူးပြု၍ သန့်ရှင်းလိုက်ပြီး ပြန်စမ်းကြည့်ပါ။"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"လက်ဗွေ သုံးခြင်း"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"လက်ဗွေ (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ရှေ့ဆက်ရန် သင့်လက်ဗွေကို သုံးပါ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"ဤစက်ပစ္စည်းတွင် မျက်နှာမှတ် သော့ဖွင့်ခြင်းကို သုံး၍မရပါ။"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
<string name="face_name_template" msgid="3877037340223318119">"မျက်နှာ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"မျက်နှာမှတ်သော့ဖွင့်ခြင်းကို သုံးခြင်း"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"မျက်နှာမှတ်သော့ဖွင့်ခြင်း (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ရှေ့ဆက်ရန် မျက်နှာမှတ်သော့ဖွင့်ခြင်းကို သုံးပါ"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"မျက်နှာသင်္ကေတ"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"သတင်းနှင့် မဂ္ဂဇင်းများ"</string>
<string name="app_category_maps" msgid="6395725487922533156">"မြေပုံနှင့် ခရီးလမ်းညွှန်ချက်"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ထုတ်လုပ်နိုင်မှု"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"အများသုံးစွဲနိုင်မှု"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"စက်ပစ္စည်း သိုလှောင်ခန်း"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB အမှားရှာပြင်ခြင်း"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"နာရီ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 1bafd6006e3c..07cf9c06f8fe 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Lar appen gjøre endringer i bildesamlingen din."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lese posisjoner fra mediesamlingen din"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Lar appen lese posisjoner fra mediesamlingen din."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Bruk biometri"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Bruk biometri eller skjermlås"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Bekreft at det er deg"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Bruk biometri for å fortsette"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvare er utilgjengelig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen er avbrutt"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ikke gjenkjent"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentiseringen er avbrutt"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-kode, mønster eller passord er ikke angitt"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Feil under autentiseringen"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Bruk skjermlås"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Skriv inn enhetslegitimasjonen din for å fortsette"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Deler av fingeravtrykket er registrert. Prøv på nytt."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingeravtrykksensoren er skitten. Rengjør den og prøv på nytt."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Bruk fingeravtrykk eller skjermlås"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Bruk fingeravtrykket ditt for å fortsette"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Ansiktslås støttes ikke på denne enheten"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensoren er midlertidig slått av."</string>
<string name="face_name_template" msgid="3877037340223318119">"Ansikt <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Bruk ansiktslås"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Bruk ansikts- eller skjermlås"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Bruk ansiktslås for å fortsette"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ansiktikon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Nyheter og tidsskrifter"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kart og navigering"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivitet"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Tilgjengelighet"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Lagring på enheten"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-feilsøking"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"time"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index a15a9a46a4d1..6295d4455ac2 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"यसले एपलाई तपाईंको तस्बिरको सङ्ग्रह परिमार्जन गर्न दिन्छ।"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"आफ्नो मिडियाको सङ्ग्रहका स्थानहरू पढ्नुहोस्‌"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"यसले एपलाई तपाईंको मिडिया सङ्ग्रहका स्थानहरू पढ्न दिन्छ।"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"बायोमेट्रिक्स प्रयोग गर्नुहोस्"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"बायोमेट्रिक्स वा स्क्रिन लक प्रयोग गर्नुहोस्"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"यो व्यक्ति तपाईं नै हो भन्ने प्रमाणित गर्नुहोस्"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"जारी राख्न आफ्नो बायोमेट्रिक प्रयोग गर्नुहोस्"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द गरियो"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"पहिचान भएन"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"प्रमाणीकरण रद्द गरियो"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"कुनै पनि PIN, ढाँचा वा पासवर्ड सेट गरिएको छैन"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"प्रमाणित गर्ने क्रममा त्रुटि भयो"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"स्क्रिन लक प्रयोग गर्नुहोस्"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"जारी राख्न आफ्नो यन्त्रको PIN, प्याटर्न वा पासवर्ड हाल्नुहोस्"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"आंशिक फिंगरप्रिन्ट पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फिंगरप्रिन्ट प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"फिंगरप्रिन्ट सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो यन्त्रमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फिंगरप्रिन्ट वा स्क्रिन लक प्रयोग गर्नुहोस्"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"जारी राख्न आफ्नो फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"यस यन्त्रमा फेस अनलक सुविधा प्रयोग गर्न मिल्दैन।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"केही समयका लागि सेन्सर असक्षम पारियो।"</string>
<string name="face_name_template" msgid="3877037340223318119">"अनुहार <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"फेस अनलक प्रयोग गर्नुहोस्"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"फेस अनलक वा स्क्रिन लक प्रयोग गर्नुहोस्"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"जारी राख्न फेस अनलक प्रयोग गर्नुहोस्"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"अनुहारको आइकन"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"समाचार तथा पत्रिकाहरू"</string>
<string name="app_category_maps" msgid="6395725487922533156">"नक्सा तथा नेभिगेसन"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"उत्पादकत्व"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"सर्वसुलभता"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"यन्त्रको भण्डारण"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB डिबग प्रक्रिया"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"घन्टा"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 1f2680e2b101..46880ea78016 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Hiermee sta je de app toe je fotocollectie aan te passen."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"locaties van je mediacollecties bekijken"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Hiermee sta je de app toe locaties van je mediacollectie te bekijken."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biometrische gegevens gebruiken"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrische gegevens of schermvergrendeling gebruiken"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Je identiteit verifiëren"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gebruik je biometrische gegevens om door te gaan"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrische hardware niet beschikbaar"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Verificatie geannuleerd"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Niet herkend"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Verificatie geannuleerd"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Geen pincode, patroon of wachtwoord ingesteld"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Fout bij verificatie"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Schermvergrendeling gebruiken"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Geef de inloggegevens van je apparaat op om door te gaan"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Gedeeltelijke vingerafdruk gedetecteerd. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"De vingerafdruksensor moet worden schoongemaakt. Probeer het daarna opnieuw."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor tijdelijk uitgeschakeld."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Vingerafdruk of schermvergrendeling gebruiken"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gebruik je vingerafdruk om door te gaan."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Ontgrendelen via gezichtsherkenning wordt niet ondersteund op dit apparaat."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor tijdelijk uitgeschakeld."</string>
<string name="face_name_template" msgid="3877037340223318119">"Gezicht <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Ontgrendelen via gezichtsherkenning gebruiken"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gezicht of schermgrendeling gebruiken"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Gebruik ontgrendelen via gezichtsherkenning om door te gaan"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Gezichtspictogram"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Nieuws en tijdschriften"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps en navigatie"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productiviteit"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Toegankelijkheid"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Apparaatopslag"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-foutopsporing"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"uur"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index e3c3c09214c8..7d65cde300bd 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"ଆପଣଙ୍କ ଫଟୋ ସଂଗ୍ରହ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପ୍‍ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଲୋକେସନ୍‍ଗୁଡିକୁ ପଢନ୍ତୁ"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଅବସ୍ଥାନଗୁଡିକୁ ପଢିବାକୁ ଆପ୍‍ ଅନୁମତି ଦେଇଥାଏ।"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ବାୟୋମେଟ୍ରିକ୍ସ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ବାୟୋମେଟ୍ରିକ୍ସ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ଏହା ଆପଣ ବୋଲି ଯାଞ୍ଚ କରନ୍ତୁ"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ବାୟୋମେଟ୍ରିକ୍ସ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ପ୍ରାମାଣିକତାକୁ ବାତିଲ୍ କରାଯାଇଛି"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ପ୍ରାମାଣିକତାକୁ ବାତିଲ୍ କରାଯାଇଛି"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"କୌଣସି ପିନ୍, ପେଟେର୍ନ ବା ପାସ୍‍ୱର୍ଡ ସେଟ୍ ନାହିଁ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ପ୍ରାମାଣିକରଣ କରିବା ସମୟରେ ତ୍ରୁଟି"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଡିଭାଇସର କ୍ରେଡେନ୍ସିଆଲ୍ ଲେଖନ୍ତୁ"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ଆଂଶିକ ଟିପଚିହ୍ନ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ଟିପଚିହ୍ନ ପ୍ରୋସେସ୍‍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"ଟିପଚିହ୍ନ ସେନ୍ସର୍‍ ମଇଳା ହୋଇଯାଇଛି। ଦୟାକରି ସଫା କରନ୍ତୁ ଓ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନ୍‍ସର୍ ନାହିଁ।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ଟିପଚିହ୍ନ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"ଏହି ଡିଭାଇସ୍‌ରେ ଫେସ୍ ଅନ୍‌ଲକ୍ ସମର୍ଥିତ ନୁହେଁ।"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
<string name="face_name_template" msgid="3877037340223318119">"<xliff:g id="FACEID">%d</xliff:g>ଙ୍କ ଫେସ୍‍"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ଫେସ୍ ଅନଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ଫେସ୍ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ଜାରି ରଖିବାକୁ ଫେସ୍ ଅନଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ଫେସ୍ ଆଇକନ୍"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ଖବର ଓ ମ୍ୟାଗାଜିନ୍‍"</string>
<string name="app_category_maps" msgid="6395725487922533156">"ମାନଚିତ୍ର ଓ ନେଭିଗେଶନ୍‍"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ଉତ୍ପାଦକତା"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ଆକ୍ସେସିବିଲିଟୀ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ଡିଭାଇସ୍‌ ଷ୍ଟୋରେଜ୍‌"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB ଡିବଗିଂ"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ଘଣ୍ଟା"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 01bf27679dc2..4b183205b14c 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Zezwala aplikacji na modyfikowanie kolekcji zdjęć."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"odczytywanie lokalizacji z kolekcji multimediów"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Zezwala aplikacji na odczytywanie lokalizacji z kolekcji multimediów."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Używaj biometrii"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Używaj biometrii lub blokady ekranu"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Potwierdź swoją tożsamość"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Użyj biometrii, by kontynuować"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Sprzęt biometryczny niedostępny"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Anulowano uwierzytelnianie"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nie rozpoznano"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Anulowano uwierzytelnianie"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nie ustawiono kodu PIN, wzoru ani hasła"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Podczas uwierzytelniania wystąpił błąd"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Używaj blokady ekranu"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Podaj dane logowania do urządzenia, aby kontynuować"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Odcisk palca został odczytany tylko częściowo. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Czytnik linii papilarnych jest zabrudzony. Wyczyść go i spróbuj ponownie."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Używaj odcisku palca lub blokady ekranu"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Użyj odcisku palca, by kontynuować"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"To urządzenie nie obsługuje rozpoznawania twarzy."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Czujnik jest tymczasowo wyłączony."</string>
<string name="face_name_template" msgid="3877037340223318119">"Twarz <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Używaj rozpoznawania twarzy"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Używaj rozpoznawania twarzy lub blokady ekranu"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Użyj rozpoznawania twarzy, aby kontynuować"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona twarzy"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Wiadomości i czasopisma"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapy i nawigacja"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktywność"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Ułatwienia dostępu"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Pamięć urządzenia"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Debugowanie USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"godz."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index aa693582f0fa..7d028f91b23f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite que o app modifique sua coleção de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ler locais na sua coleção de mídias"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que o app leia os locais na sua coleção de mídias."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Usar biometria"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar biometria ou bloqueio de tela"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirme que é você"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use seus dados biométricos para continuar"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Não reconhecido"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticação cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenhum PIN, padrão ou senha configurado"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erro na autenticação"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueio de tela"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Insira as credenciais do dispositivo para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Impressão digital parcial detectada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use sua impressão digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"O desbloqueio facial não é compatível com este dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor desativado temporariamente."</string>
<string name="face_name_template" msgid="3877037340223318119">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usar desbloqueio facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar reconhecimento facial ou bloqueio de tela"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Use o desbloqueio facial para continuar"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ícone facial"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Notícias e revistas"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapas e navegação"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produtividade"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Acessibilidade"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Armazenamento do dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuração USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 232a17219eb4..a01db5e3fca6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite que a app modifique a sua coleção de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ler as localizações a partir da sua coleção de multimédia"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que a app leia as localizações a partir da sua coleção de multimédia."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Utilizar a biometria"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utilizar a biometria ou o bloqueio de ecrã"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirme a sua identidade"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilize a biometria para continuar."</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível."</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Não reconhecido."</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticação cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenhum PIN, padrão ou palavra-passe definidos."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erro ao autenticar."</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utilizar o bloqueio de ecrã"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Introduza a credencial do dispositivo para continuar."</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Impressão digital parcial detetada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"O sensor de impressões digitais está sujo. Limpe-o e tente novamente."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar a impressão digital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar o bloqueio de ecrã ou a impressão digital"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilize a sua impressão digital para continuar."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Desbloqueio facial não suportado neste dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor temporariamente desativado."</string>
<string name="face_name_template" msgid="3877037340223318119">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Utilizar o desbloqueio facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Utilizar o bloqueio através do rosto ou de ecrã"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Utilize o desbloqueio facial para continuar."</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ícone de rosto"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Notícias e revistas"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapas e navegação"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produtividade"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Acessibilidade"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Armazenamento do dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuração USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index aa693582f0fa..7d028f91b23f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite que o app modifique sua coleção de fotos."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"ler locais na sua coleção de mídias"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que o app leia os locais na sua coleção de mídias."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Usar biometria"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar biometria ou bloqueio de tela"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirme que é você"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use seus dados biométricos para continuar"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Não reconhecido"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticação cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenhum PIN, padrão ou senha configurado"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erro na autenticação"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueio de tela"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Insira as credenciais do dispositivo para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Impressão digital parcial detectada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use sua impressão digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"O desbloqueio facial não é compatível com este dispositivo."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensor desativado temporariamente."</string>
<string name="face_name_template" msgid="3877037340223318119">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Usar desbloqueio facial"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Usar reconhecimento facial ou bloqueio de tela"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Use o desbloqueio facial para continuar"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ícone facial"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Notícias e revistas"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapas e navegação"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produtividade"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Acessibilidade"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Armazenamento do dispositivo"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Depuração USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hora"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 01daa557ca99..e8c553908c29 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -553,23 +553,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite aplicației să vă modifice colecția de fotografii."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"citiți locațiile din colecția media"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite aplicației să citească locațiile din colecția dvs. media."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Folosiți sistemele biometrice"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Folosiți sistemele biometrice sau blocarea ecranului"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmați-vă identitatea"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Folosiți sistemele biometrice pentru a continua"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometric indisponibil"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentificarea a fost anulată"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nu este recunoscut"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentificarea a fost anulată"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nu este setat niciun cod PIN, model sau parolă"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Eroare la autentificare"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Folosiți blocarea ecranului"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Introduceți datele de conectare ale dispozitivului pentru a continua"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"S-a detectat parțial amprenta. Încercați din nou."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Senzorul pentru amprente este murdar. Curățați-l și încercați din nou."</string>
@@ -592,10 +587,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosiți amprenta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosiți amprenta sau blocarea ecranului"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosiți amprenta pentru a continua"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -641,12 +634,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Deblocarea facială nu este acceptată pe dispozitiv."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzorul este dezactivat temporar."</string>
<string name="face_name_template" msgid="3877037340223318119">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Folosiți deblocarea facială"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosiți blocarea ecranului sau blocarea facială"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Folosiți deblocarea facială pentru a continua"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Pictograma chip"</string>
@@ -1998,8 +1988,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Știri și reviste"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Hărți și navigare"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivitate"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Accesibilitate"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Stocare pe dispozitiv"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Remedierea erorilor prin USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"oră"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1a513e462580..48f96ef3d289 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Приложение сможет вносить изменения в вашу фотоколлекцию."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"доступ к геоданным в медиаколлекции"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Приложение получит доступ к геоданным в вашей медиаколлекции."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Использовать биометрию"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Использовать биометрию или блокировку экрана"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Подтвердите, что это вы"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Чтобы продолжить, используйте биометрические данные"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрическое оборудование недоступно"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аутентификация отменена"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не распознано"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аутентификация отменена"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Укажите PIN-код, пароль или графический ключ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Ошибка аутентификации."</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Использовать блокировку экрана"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Чтобы продолжить, введите учетные данные устройства"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Отсканирована только часть отпечатка. Повторите попытку."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не удалось распознать отпечаток. Повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Очистите сканер и повторите попытку."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Использовать отпечаток пальца"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Использовать отпечаток пальца или блокировку экрана"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Чтобы продолжить, используйте цифровой отпечаток"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Это устройство не поддерживает функцию \"Фейсконтроль\"."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Датчик временно отключен."</string>
<string name="face_name_template" msgid="3877037340223318119">"Лицо <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Использовать фейсконтроль"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Использовать фейсконтроль или блокировку экрана"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Чтобы продолжить, используйте фейсконтроль"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Значок лица"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Новости и журналы"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карты и навигация"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Работа"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Доступность"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Хранилище устройства"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отладка по USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ч."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 011b042f81b3..f645fe10f3bc 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Umožňuje aplikácii upravovať zbierku fotiek."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"čítať polohy zo zbierky médií"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Umožňuje aplikácii čítať polohy zo zbierky médií."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Použiť biometrické údaje"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Použiť biometrické údaje alebo zámku obrazovky"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Overenie, že ste to vy"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Ak chcete pokračovať, použite biometrický údaj"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardvér nie je k dispozícii"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Overenie bolo zrušené"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nerozpoznané"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Overenie bolo zrušené"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nie je nastavený PIN, vzor ani heslo"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Chyba overenia"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Použiť zámku obrazovky"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Ak chcete pokračovať, zadajte prihlasovacie údaje zariadenia"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Očistite senzor odtlačkov prstov a skúste to znova."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použiť odtlačok prsta alebo zámku obrazovky"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Pokračujte nasnímaním odtlačku prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Toto zariadenie nepodporuje odomknutie tvárou."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzor je dočasne vypnutý."</string>
<string name="face_name_template" msgid="3877037340223318119">"Tvár <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Použiť odomknutie tvárou"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Použiť tvár alebo zámku obrazovky"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Ak chcete pokračovať, použite odomknutie tvárou"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona tváre"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Noviny a časopisy"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mapy a navigácia"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Kancelárske"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Dostupnosť"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Úložisko zariadenia"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Ladenie cez USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"hodina"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0a7f7600abfc..97de6a370883 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Aplikaciji omogoča spreminjanje zbirke fotografij."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"branje lokacij v predstavnostni zbirki"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Aplikaciji omogoča branje lokacij v predstavnostni zbirki."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Uporaba biometrike"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Uporaba biometrike ali odklepanja s poverilnico"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Preverite, da ste res vi"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Za nadaljevanje uporabite biometrični podatek."</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Strojna oprema za biometrične podatke ni na voljo"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Preverjanje pristnosti je preklicano"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ni prepoznano"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Preverjanje pristnosti je preklicano"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nastavljena ni nobena koda PIN, vzorec ali geslo"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Napaka pri preverjanju pristnosti"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Uporaba odklepanja s poverilnico"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Za nadaljevanje vnesite poverilnico za napravo."</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Zaznan delni prstni odtis. Poskusite znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Tipalo prstnih odtisov je umazano. Očistite ga in poskusite znova."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Uporaba prstnega odtisa ali odklepanja s poverilnico"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Uporabite prstni odtis, če želite nadaljevati."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Ta naprava ne podpira odklepanja z obrazom."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Tipalo je začasno onemogočeno."</string>
<string name="face_name_template" msgid="3877037340223318119">"Obraz <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Uporaba odklepanja z obrazom"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Uporaba odklepanja z obrazom ali s poverilnico"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Za nadaljevanje uporabite odklepanje z obrazom."</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona obraza"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Novice in revije"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Zemljevidi in navigacija"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Storilnost"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Funkc. za ljudi s poseb. potreb."</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Shramba naprave"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Odpravljanje težav prek povezave USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ura"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index ddce26d68e59..a78ff777258e 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Lejon aplikacionin të modifikojë koleksionin tënd të fotografive."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"lexo vendndodhjet nga koleksioni yt i medias"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Lejon aplikacionin të lexojë vendndodhjet nga koleksioni yt i medias."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Përdor sistemet biometrike"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Përdor sistemet biometrike ose kyçjen e ekranit"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifiko që je ti"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Përdor sistemet e tua biometrike për të vazhduar"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Nuk ofrohet harduer biometrik"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Vërtetimi u anulua"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nuk njihet"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Vërtetimi u anulua"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nuk është vendosur kod PIN, motiv ose fjalëkalim"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Gabim gjatë vërtetimit"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Përdor kyçjen e ekranit"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Fut kredencialet e pajisjes për të vazhduar"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"U zbulua një gjurmë gishti e pjesshme. Provo përsëri."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Gjurma e gishtit nuk mund të përpunohej. Provo përsëri."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Sensori i gjurmës së gishtit nuk është i pastër. Pastroje dhe provo përsëri."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Përdor gjurmën e gishtit ose kyçjen e ekranit"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Përdor gjurmën e gishtit për të vazhduar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Shkyçja me fytyrë nuk mbështetet në këtë pajisje"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensori është çaktivizuar përkohësisht."</string>
<string name="face_name_template" msgid="3877037340223318119">"Fytyra <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Përdor shkyçjen me fytyrë"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Përdor kyçjen me fytyrë ose kyçjen e ekranit"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Përdor shkyçjen me fytyrë për të vazhduar"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikona e fytyrës"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Lajme dhe revista"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Harta dhe navigim"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivitet"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Qasshmëria"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Hapësira ruajtëse e pajisjes"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Korrigjimi përmes USB-së"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"orë"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 11d5e7d8c43c..04bc35f5a2d1 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -553,23 +553,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Дозвољава апликацији да мења колекцију слика."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"читање локација из медијске колекције"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Дозвољава апликацији да чита локације из медијске колекције."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Користите биометрију"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Користите биометрију или закључавање екрана"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдите свој идентитет"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Користите биометријски податак да бисте наставили"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометријски хардвер није доступан"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Потврда идентитета је отказана"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Није препознато"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Потврда идентитета је отказана"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Нисте подесили ни PIN, ни шаблон, ни лозинку"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при потврди идентитета"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Користите закључавање екрана"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Унесите акредитив за уређај да бисте наставили"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Откривен је делимични отисак прста. Пробајте поново."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Није успела обрада отиска прста. Пробајте поново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сензор за отиске прстију је прљав. Очистите га и покушајте поново."</string>
@@ -592,10 +587,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користите отисак прста или закључавање екрана"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Наставите помоћу отиска прста"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -641,12 +634,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Откључавање лицем није подржано на овом уређају"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Сензор је привремено онемогућен."</string>
<string name="face_name_template" msgid="3877037340223318119">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Користите откључавање лицем"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Користите закључавање лицем или закључавање екрана"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Користите откључавање лицем да бисте наставили"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Икона лица"</string>
@@ -1998,8 +1988,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Новости и часописи"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Мапе и навигација"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Продуктивност"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Приступачност"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Меморијски простор уређаја"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отклањање грешака са USB-а"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"сат"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4f6ccff5313f..5ab243a042e1 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Tillåter att appen gör ändringar i din fotosamling."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"läsa av platser i din mediesamling"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Tillåter att appen läser av platser i din mediesamling."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Använd biometri"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Använd biometrisk data eller skärmlåset"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifiera din identitet"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Fortsätt med hjälp av din biometriska data"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvara är inte tillgänglig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen avbröts"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Identifierades inte"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentiseringen avbröts"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Pinkod, grafiskt lösenord eller lösenord har inte angetts"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Ett fel uppstod vid autentiseringen"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Använd skärmlåset"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Fortsätt genom att ange enhetens autentiseringsuppgifter"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Ofullständigt fingeravtryck. Försök igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Fingeravtryckssensorn är smutsig. Rengör den och försök igen."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Använd ditt fingeravtryck eller skärmlåset"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Fortsätt med hjälp av ditt fingeravtryck"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Ansiktslås stöds inte på den här enheten."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensorn har tillfälligt inaktiverats."</string>
<string name="face_name_template" msgid="3877037340223318119">"Ansikte <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Använd ansiktslåset"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Använd ansiktslåset eller skärmlåset"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Fortsätt med hjälp av ansiktslåset"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ansikte"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Nyheter och tidskrifter"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Kartor och navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Produktivitet"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Tillgänglighet"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Enhetens lagringsutrymme"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-felsökning"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"timme"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8dd721f8890a..00477d1b1f5f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Inaruhusu programu kubadilisha mkusanyiko wa picha zako."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"kusoma maeneo kwenye mkusanyiko wa vipengee vyako"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Inaruhusu programu kusoma maeneo kwenye mkusanyiko wa vipengee vyako."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Tumia bayometriki"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Tumia bayometriki au mbinu ya kufunga skrini"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Thibitisha kuwa ni wewe"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Tumia bayometriki yako ili uendelee"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Maunzi ya bayometriki hayapatikani"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Imeghairi uthibitishaji"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Hayatambuliki"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Imeghairi uthibitishaji"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Hujaweka pin, mchoro au nenosiri"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Hitilafu imetokea wakati wa uthibitishaji"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Tumia mbinu ya kufunga skrini"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Weka kitambulisho cha kifaa chako ili uendelee."</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Kitambua alama kimetambua sehemu ya alama. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Imeshindwa kuchakata alama ya kidole. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Kitambua alama ya kidole ni kichafu. Tafadhali kisafishe na ujaribu tena."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Tumia alama ya kidole au mbinu ya kufunga skrini"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Tumia alama ya kidole chako ili uendelee"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Kufungua kwa uso hakutumiki kwenye kifaa hiki."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Kitambuzi kimezimwa kwa muda."</string>
<string name="face_name_template" msgid="3877037340223318119">"Uso wa <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Tumia kipengele cha kufungua kwa uso"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Tumia uso au mbinu ya kufunga skrini"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Tumia kipengele cha kufungua kwa uso ili uendelee"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Aikoni ya uso"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Habari na Magazeti"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Ramani na Maelekezo"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Uzalishaji"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Ufikivu"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Hifadhi ya kifaa"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Utatuzi wa USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"saa"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 3760c6dc73c5..6325c191eea3 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"உங்களின் படத் தொகுப்பை மாற்ற ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"மீடியா தொகுப்பிலிருந்து இடங்களை அறிதல்"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"உங்களின் மீடியா தொகுப்பிலிருந்து இடங்களை அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கும்."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"பயோமெட்ரிக்ஸைப் பயன்படுத்து"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"பயோமெட்ரிக்ஸையோ திரைப் பூட்டையோ பயன்படுத்து"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"நீங்கள்தான் என உறுதிசெய்க"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"தொடர உங்கள் பயோமெட்ரிக்கைப் பயன்படுத்துங்கள்"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"அங்கீகரிப்பு ரத்தானது"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"அடையாளங்காணபடவில்லை"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"அங்கீகரிப்பு ரத்தானது"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"பின்னோ, பேட்டர்னோ, கடவுச்சொல்லோ அமைக்கப்படவில்லை"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"அங்கீகரிப்பதில் பிழை"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"திரைப் பூட்டைப் பயன்படுத்து"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"தொடர, சாதன அனுமதிச் சான்றை உள்ளிடுங்கள்"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"கைரேகையை ஓரளவுதான் கண்டறிய முடிந்தது. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"கைரேகை சென்சாரில் தூசி உள்ளது. சுத்தம் செய்து, முயலவும்."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"கைரேகையைப் பயன்படுத்து"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"கைரேகையையோ திரைப் பூட்டையோ பயன்படுத்து"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"தொடர்வதற்கு கைரேகையைப் பயன்படுத்துங்கள்"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"இந்த சாதனத்தில் ’முகம் காட்டித் திறத்தல்’ ஆதரிக்கப்படவில்லை."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
<string name="face_name_template" msgid="3877037340223318119">"முகம் <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"முகம் காட்டித் திறத்தலை உபயோகி"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"முகத்தையோ திரைப் பூட்டையோ பயன்படுத்து"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"தொடர, \'முகம் காட்டித் திறத்தல்\' அம்சத்தை உபயோகியுங்கள்"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"முக ஐகான்"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"செய்திகளும் பத்திரிகைகளும்"</string>
<string name="app_category_maps" msgid="6395725487922533156">"வரைபடங்களும் வழிசெலுத்தலும்"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"உற்பத்தித்திறன்"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"அணுகல்தன்மை"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"சாதனச் சேமிப்பகம்"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB பிழைதிருத்தம்"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"மணி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 63ee3a0374af..c2a5d42b6a25 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవండి"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"బయోమెట్రిక్స్‌ను ఉపయోగించండి"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"బయోమెట్రిక్స్‌ను లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ఇది మీరేనని వెరిఫై చేసుకోండి"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"కొనసాగించడానికి, మీ బయోమెట్రిక్‌ను ఉపయోగించండి"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"బయోమెట్రిక్ హార్డ్‌వేర్‌ అందుబాటులో లేదు"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ప్రమాణీకరణ రద్దు చేయబడింది"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"గుర్తించలేదు"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ప్రమాణీకరణ రద్దు చేయబడింది"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"పిన్, ఆకృతి లేదా పాస్‌వర్డ్‌ సెట్ చేయబడలేదు"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ప్రామాణీకరిస్తున్నప్పుడు ఎర్రర్ ఏర్పడింది"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"కొనసాగడానికి, మీ పరికర ఆధారాలను ఎంటర్ చేయండి"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"పాక్షిక వేలిముద్ర గుర్తించబడింది. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"వేలిముద్ర సెన్సార్ మురికిగా ఉంది. దయచేసి శుభ్రపరిచి, మళ్లీ ప్రయత్నించండి."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"వేలు <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"వేలిముద్రను ఉపయోగించండి"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"వేలిముద్ర లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"కొనసాగించడానికి మీ వేలిముద్రను ఉపయోగించండి"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"ఈ పరికరంలో ముఖంతో అన్‌లాక్‌ను ఉపయోగించడానికి మద్దతు లేదు."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
<string name="face_name_template" msgid="3877037340223318119">"ముఖ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ఫేస్ అన్‌లాక్‌ను ఉపయోగించండి"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ముఖం లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"కొనసాగించడానికి, మీ ఫేస్ అన్‌లాక్‌ను ఉపయోగించండి"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ముఖ చిహ్నం"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"వార్తలు &amp; వార్తాపత్రికలు"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; నావిగేషన్"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ఉత్పాదకత"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"యాక్సెసిబిలిటీ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"పరికర నిల్వ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB డీబగ్గింగ్"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"గంట"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 3c985b999162..a5cfe4028a39 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"อนุญาตให้แอปแก้ไขคอลเล็กชันรูปภาพของคุณ"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"อ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"อนุญาตให้แอปอ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"ใช้ข้อมูลไบโอเมตริก"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ใช้ข้อมูลไบโอเมตริกหรือการล็อกหน้าจอ"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"ยืนยันว่าเป็นตัวคุณ"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ใช้ข้อมูลไบโอเมตริกเพื่อดำเนินการต่อ"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ฮาร์ดแวร์ไบโอเมตริกไม่พร้อมใช้งาน"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ยกเลิกการตรวจสอบสิทธิ์แล้ว"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ไม่รู้จัก"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ยกเลิกการตรวจสอบสิทธิ์แล้ว"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ไม่ได้ตั้ง PIN, รูปแบบ หรือรหัสผ่าน"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"การตรวจสอบข้อผิดพลาด"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ใช้การล็อกหน้าจอ"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"ป้อนข้อมูลเข้าสู่ระบบอุปกรณ์เพื่อดำเนินการต่อ"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"เซ็นเซอร์ลายนิ้วมือไม่สะอาด โปรดทำความสะอาดและลองอีกครั้ง"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้ว <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ใช้ลายนิ้วมือหรือการล็อกหน้าจอ"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ใช้ลายนิ้วมือของคุณเพื่อดำเนินการต่อ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"อุปกรณ์นี้ไม่รองรับการปลดล็อกด้วยใบหน้า"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
<string name="face_name_template" msgid="3877037340223318119">"ใบหน้า <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"ใช้การปลดล็อกด้วยใบหน้า"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"ใช้การล็อกด้วยใบหน้าหรือการล็อกหน้าจอ"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"ใช้การปลดล็อกด้วยใบหน้าเพื่อดำเนินการต่อ"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ไอคอนใบหน้า"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"ข่าวสารและนิตยสาร"</string>
<string name="app_category_maps" msgid="6395725487922533156">"แผนที่และการนำทาง"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ประสิทธิภาพการทำงาน"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"การช่วยเหลือพิเศษ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"พื้นที่เก็บข้อมูลของอุปกรณ์"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"การแก้ไขข้อบกพร่อง USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ชั่วโมง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7402195a0de8..4829cefe1940 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Pinapayagan ang app na baguhin ang iyong koleksyon ng larawan."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"basahin ang mga lokasyon mula sa iyong koleksyon ng media"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Pinapayagan ang app na basahin ang mga lokasyon mula sa iyong koleksyon ng media."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Gumamit ng biometrics"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gumamit ng biometrics o lock ng screen"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"I-verify na ikaw ito"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gamitin ang iyong biometric para magpatuloy"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Walang biometric hardware"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Nakansela ang pag-authenticate"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Hindi nakilala"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Nakansela ang pag-authenticate"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Walang itinakdang pin, pattern, o password"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Nagkaroon ng error sa pag-authenticate"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gumamit ng lock ng screen"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Ilagay ang kredensyal ng device para magpatuloy"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Hindi buo ang natukoy na fingerprint. Pakisubukan ulit."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Hindi maproseso ang fingerprint. Pakisubukan ulit."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Marumi ang sensor ng fingerprint. Pakilinis at subukang muli."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gumamit ng fingerprint o lock ng screen"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gamitin ang iyong fingerprint para magpatuloy"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Hindi sinusuportahan ang face unlock sa device na ito."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Pansamantalang na-disable ang sensor."</string>
<string name="face_name_template" msgid="3877037340223318119">"Mukha <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Gumamit ng face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Gumamit ng mukha o lock ng screen"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Gumamit ng face unlock para magpatuloy"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Face icon"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Balita at Mga Magazine"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Mga Mapa at Navigation"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productivity"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Pagiging Accessible"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Storage ng device"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Pag-debug ng USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"oras"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 147553fbfa0a..186ffa7c8206 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Uygulamanın fotoğraf koleksiyonunuzu değiştirmesine izin verir."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"medya koleksiyonunuzdaki konumları okuma"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Uygulamanın medya koleksiyonunuzdaki konumları okumasına izin verir."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biyometri kullan"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biyometri veya ekran kilidi kullan"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Siz olduğunuzu doğrulayın"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Devam etmek için biyometri kullanın"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biyometrik donanım kullanılamıyor"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Kimlik doğrulama iptal edildi"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tanınmadı"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Kimlik doğrulama iptal edildi"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN, desen veya şifre seti yok"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Kimlik doğrulama sırasında hata oluştu"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekran kilidi kullan"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Devam etmek için cihaz kimlik bilginizi girin"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Parmak izinin tümü algılanamadı. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Parmak izi sensörü kirli. Lütfen temizleyin ve tekrar deneyin."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Parmak izi veya ekran kilidi kullan"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Devam etmek için parmak izinizi kullanın"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Bu cihazda yüz tanıma kilidi desteklenmiyor"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Sensör geçici olarak devre dışı bırakıldı."</string>
<string name="face_name_template" msgid="3877037340223318119">"Yüz <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Yüz tanıma kilidi kullan"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Yüz tanıma veya ekran kilidi kullan"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Devam etmek için yüz tanıma kilidini kullanın"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Yüz simgesi"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Haberler ve Dergiler"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Haritalar ve Navigasyon"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Verimlilik"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Erişilebilirlik"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Cihazdaki depolama alanı"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB üzerinden hata ayıklama"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"saat"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 77e664afcbfe..7049a2e11110 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -556,23 +556,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Додаток зможе змінювати вашу колекцію фотографій."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"розпізнавати геодані з колекції медіа-вмісту"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Додаток зможе розпізнавати геодані з вашої колекції медіа-вмісту."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Використовувати біометрію"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Використовувати біометрію або блокування екрана"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Підтвердьте, що це ви"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Щоб продовжити, скористайтеся біометрією"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Біометричне апаратне забезпечення недоступне"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Автентифікацію скасовано"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не розпізнано"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Автентифікацію скасовано"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не вказано PIN-код, ключ або пароль"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Помилка автентифікації"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Використовувати блокування екрана"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Щоб продовжити, введіть облікові дані пристрою"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Відбиток пальця розпізнано частково. Повторіть спробу."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Датчик відбитків пальців забруднився. Очистьте його та повторіть спробу."</string>
@@ -595,10 +590,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Використовувати відбиток пальця"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Використовувати відбиток пальця або блокування екрана"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Щоб продовжити, скористайтеся своїм відбитком пальця"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -644,12 +637,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"На цьому пристрої не підтримується Фейсконтроль."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Датчик тимчасово вимкнено."</string>
<string name="face_name_template" msgid="3877037340223318119">"Обличчя <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Використовувати фейсконтроль"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Використовувати фейсконтроль або блокування екрана"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Щоб продовжити, скористайтеся фейсконтролем"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Значок обличчя"</string>
@@ -2030,8 +2020,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Новини та журнали"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Карти й навігація"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Продуктивність"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Спеціальні можливості"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Пам’ять пристрою"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Налагодження USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"години"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 10f787b70fd1..615e2fa4a831 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"ایپ کو آپ کی تصویر کے مجموعے میں ترمیم کی اجازت دیتا ہے۔"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"اپنی میڈيا کے مجموعے سے مقامات پڑھیں"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"ایپ کو آپ کی میڈيا کے مجموعے سے مقامات پڑھنے کی اجازت دیتا ہے۔"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"بایو میٹرکس استعمال کریں"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"بایو میٹرکس یا اسکرین لاک استعمال کریں"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"توثیق کریں کہ یہ آپ ہیں"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"جاری رکھنے کیلئے اپنا بایو میٹرک استعمال کریں"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"تصدیق کا عمل منسوخ ہو گیا"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"تسلیم شدہ نہیں ہے"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"تصدیق کا عمل منسوخ ہو گیا"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"کوئی پن، پیٹرن، یا پاس ورڈ سیٹ نہیں ہے"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"خرابی کی توثیق ہو رہی ہے"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"اسکرین لاک استعمال کریں"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"جاری رکھنے کیلئے اپنے آلے کی سند درج کریں"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"جزوی فنگر پرنٹ کی شناخت ہوئی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"فنگر پرنٹ سینسر گندا ہے۔ براہ کرم صاف کریں اور دوبارہ کوشش کریں۔"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"فنگر پرنٹ استعمال کریں"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"فنگر پرنٹ یا اسکرین لاک استعمال کریں"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"جاری رکھنے کیلئے اپنا فنگر پرنٹ استعمال کریں"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"اس آلہ پر چہرے کے ذریعے غیر مقفل کرنا تعاون یافتہ نہیں ہے۔"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"سینسر عارضی طور غیر فعال ہے۔"</string>
<string name="face_name_template" msgid="3877037340223318119">"چہرہ <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"چہرے کے ذریعے غیر مقفل کرنا استعمال کریں"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"چہرہ یا اسکرین لاک استعمال کریں"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"جاری رکھنے کیلئے چہرے کے ذریعے غیر مقفل کرنا استعمال کریں"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"چہرے کا آئیکن"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"خبریں اور میگزین"</string>
<string name="app_category_maps" msgid="6395725487922533156">"نقشے اور نیویگیشن"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"پروڈکٹیوٹی"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"ایکسیسبیلٹی"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"آلہ کی اسٹوریج"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"‏USB ڈیبگ کرنا"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"گھنٹہ"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index f2c5589f392b..0bcba068ffa3 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -553,7 +553,7 @@
<string name="biometric_app_setting_name" msgid="3339209978734534457">"Biometrik tasdiqlash"</string>
<string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrika yoki ekran qulfi"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Oʻzingizni taniting"</string>
- <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Davob etish uchun biometrik tasdiqlang"</string>
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Davom etish uchun biometrik tasdiqlang"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrik sensor ishlamayapti"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikatsiya bekor qilindi"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Aniqlanmadi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4d7091c68696..11644d6dba9a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"đọc vị trí từ bộ sưu tập phương tiện"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Dùng dữ liệu sinh trắc học"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Dùng dữ liệu sinh trắc học hoặc phương thức khóa màn hình"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Dùng dữ liệu sinh trắc học của bạn để tiếp tục"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Đã hủy xác thực"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Chưa đặt mã PIN, hình mở khóa hoặc mật khẩu"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Lỗi khi xác thực"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Dùng phương thức khóa màn hình"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Nhập thông tin xác thực thiết bị của bạn để tiếp tục"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Đã phát hiện được một phần vân tay. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Cảm biến vân tay bị bẩn. Hãy làm sạch và thử lại."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Dùng vân tay hoặc phương thức khóa màn hình"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Sử dụng dấu vân tay của bạn để tiếp tục"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"Thiết bị này không hỗ trợ tính năng mở khóa bằng khuôn mặt."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Đã tạm thời tắt cảm biến."</string>
<string name="face_name_template" msgid="3877037340223318119">"Khuôn mặt <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Dùng tính năng mở khóa bằng khuôn mặt"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Dùng khuôn mặt hoặc phương thức khóa màn hình"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Dùng tính năng mở khóa bằng khuôn mặt để tiếp tục"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Biểu tượng khuôn mặt"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Tin tức và tạp chí"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Bản đồ và dẫn đường"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Sản xuất"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Hỗ trợ tiếp cận"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Bộ nhớ của thiết bị"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Gỡ lỗi qua USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"giờ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1f8f1766b376..ec455bc07ab0 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"允许该应用修改您的照片收藏。"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"从您的媒体收藏中读取位置信息"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"允许该应用从您的媒体收藏中读取位置信息。"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物识别"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物识别或屏幕锁定凭据"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"验证是您本人在操作"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"使用生物识别验证身份才能继续"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"生物识别硬件无法使用"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"身份验证已取消"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"无法识别"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"身份验证已取消"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"未设置任何 PIN 码、图案和密码"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"进行身份验证时出错"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用屏幕锁定凭据"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"输入您的设备凭据才能继续"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"仅检测到部分指纹,请重试。"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"无法处理指纹,请重试。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"指纹传感器有脏污。请擦拭干净,然后重试。"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指纹"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指纹或屏幕锁定凭据"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"使用指纹完成验证才能继续"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"此设备不支持人脸解锁。"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"传感器已暂时停用。"</string>
<string name="face_name_template" msgid="3877037340223318119">"面孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"使用人脸解锁"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"使用人脸解锁或屏幕锁定凭据"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"使用人脸解锁验证身份才能继续"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"面孔图标"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"新闻和杂志"</string>
<string name="app_category_maps" msgid="6395725487922533156">"地图和导航"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"办公"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"无障碍"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"设备存储空间"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB 调试"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"点"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index ebc02c502b8a..16b9b0fe31e4 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"允許應用程式修改您的相片集。"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"讀取媒體集的位置"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"允許應用程式讀取媒體集的位置。"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物識別"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物識別或螢幕鎖定"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"驗證是你本人"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用使用生物識別驗證身分"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物識別硬件"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"未能識別"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"已取消驗證"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"未設定 PIN、圖案或密碼"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"驗證時發生錯誤"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用螢幕鎖定"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"如要繼續操作,請輸入裝置憑證"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"只偵測到部分指紋。請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"無法處理指紋。請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"指紋感應器不乾淨。請清潔後再試一次。"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋鎖定"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指紋或螢幕鎖定"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"請使用您的指紋繼續"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"此裝置不支援「臉孔解鎖」。"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"感應器已暫時停用。"</string>
<string name="face_name_template" msgid="3877037340223318119">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"使用臉孔解鎖"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"使用臉孔或螢幕鎖定"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"如要繼續操作,請使用臉孔解鎖驗證身分"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"臉孔圖示"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"新聞和雜誌"</string>
<string name="app_category_maps" msgid="6395725487922533156">"地圖和導航"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"生產力應用程式"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"無障礙功能"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"裝置儲存空間"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB 偵錯"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"時"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 10d05c1fa14d..90fe15d144c4 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"允許應用程式修改你的相片收藏。"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"讀取你的媒體收藏的位置資訊"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"允許應用程式讀取你的媒體收藏的位置資訊。"</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物特徵辨識功能"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物特徵辨識或螢幕鎖定功能"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"驗證你的身分"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用生物特徵辨識功能驗證身分"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物特徵辨識硬體"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"無法辨識"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"已取消驗證"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"未設定 PIN 碼、解鎖圖案或密碼"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"驗證時發生錯誤"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用螢幕鎖定功能"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"如要繼續操作,請輸入裝置憑證"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"僅偵測到部分指紋,請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"無法處理指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指紋或螢幕鎖定功能"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"使用指紋完成驗證才能繼續操作"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"這個裝置不支援人臉解鎖功能。"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"感應器已暫時停用。"</string>
<string name="face_name_template" msgid="3877037340223318119">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"使用人臉解鎖功能"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"使用人臉解鎖或螢幕鎖定功能"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"如要繼續操作,請使用人臉解鎖功能驗證身分"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"臉孔圖示"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"新聞和雜誌"</string>
<string name="app_category_maps" msgid="6395725487922533156">"地圖和導航"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"工作效率"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"無障礙工具"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"裝置儲存空間"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB 偵錯"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"點"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 976b97a2b8ed..1089f1e4b1c5 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -550,23 +550,18 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Ivumela uhlelo lwakho lokusebenza ukuthi lilungise iqoqo lakho lesithombe."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"funda izindawo kusukela kuqoqo lakho lemidiya"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Ivumela uhlelo lokusebenza ukuthi lifunde izindawo kusukela kuqoqo lakho lemidiya."</string>
- <!-- no translation found for biometric_app_setting_name (3339209978734534457) -->
- <skip />
- <!-- no translation found for biometric_or_screen_lock_app_setting_name (5348462421758257752) -->
- <skip />
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Sebenzisa i-biometrics"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Sebenzisa i-biometrics noma ukukhiya isikrini"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Qinisekisa ukuthi nguwe"</string>
- <!-- no translation found for biometric_dialog_default_subtitle (8457232339298571992) -->
- <skip />
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Sebenzisa i-biometric yakho ukuze uqhubeke"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"I-Biometric hardware ayitholakali"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ukufakazela ubuqiniso kukhanseliwe"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Akwaziwa"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Ukufakazela ubuqiniso kukhanseliwe"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ayikho iphinikhodi, iphethini, noma iphasiwedi esethiwe"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Iphutha lokufakazela ubuqiniso"</string>
- <!-- no translation found for screen_lock_app_setting_name (6054944352976789228) -->
- <skip />
- <!-- no translation found for screen_lock_dialog_default_subtitle (8638638125397857315) -->
- <skip />
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Sebenzisa isikhiya sesikrini"</string>
+ <string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"Faka izifakazelo zedivayisi yakho ukuze uqhubeke"</string>
<string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Izigxivizo zeminwe ezincane zitholiwe. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Inzwa yezigxivizo zeminwe ingcolile. Sicela uyihlanze uphinde uzame futhi."</string>
@@ -589,10 +584,8 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string>
- <!-- no translation found for fingerprint_app_setting_name (4253767877095495844) -->
- <skip />
- <!-- no translation found for fingerprint_or_screen_lock_app_setting_name (3501743523487644907) -->
- <skip />
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sebenzisa izigxivizo zeminwe noma ukukhiya isikrini"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Sebenzisa izigxivizo zakho zeminwe ukuze uqhubeke"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -638,12 +631,9 @@
<string name="face_error_hw_not_present" msgid="1070600921591729944">"I-face unlock ayisekelwe kule divayisi."</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Inzwa ikhutshazwe okwesikhashana."</string>
<string name="face_name_template" msgid="3877037340223318119">"Ubuso be-<xliff:g id="FACEID">%d</xliff:g>"</string>
- <!-- no translation found for face_app_setting_name (8130135875458467243) -->
- <skip />
- <!-- no translation found for face_or_screen_lock_app_setting_name (1603149075605709106) -->
- <skip />
- <!-- no translation found for face_dialog_default_subtitle (4979205739418564856) -->
- <skip />
+ <string name="face_app_setting_name" msgid="8130135875458467243">"Sebenzisa i-face unlock"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Sebenzisa i-face lock noma ukukhiya isikrini"</string>
+ <string name="face_dialog_default_subtitle" msgid="4979205739418564856">"Sebenzisa i-face unlock ukuze uqhubeke"</string>
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Isithonjana sobuso"</string>
@@ -1966,8 +1956,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Izindaba nomagazini"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Amamephu nokuzula"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Ukukhiqiza"</string>
- <!-- no translation found for app_category_accessibility (6643521607848547683) -->
- <skip />
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Ukufinyeleleka"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Isitoreji sedivayisi"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Ukulungisa iphutha le-USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ihora"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e0a116be371c..9e1a08527330 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2194,6 +2194,7 @@
Note that even if no splashscreen content is set on the theme, the system may still
show a splash screen using the other attributes on the theme, like the
{@link android.R.attr#windowBackground}.
+ {@deprecated Use windowSplashscreenAnimatedIcon instead.}
-->
<attr name="windowSplashscreenContent" format="reference" />
@@ -3564,6 +3565,7 @@
<attr name="__removed2" format="boolean" />
<!-- Specifies whether the IME supports showing inline suggestions. -->
<attr name="supportsInlineSuggestions" format="boolean" />
+ <attr name="suppressesSpellChecker" format="boolean" />
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
@@ -8163,7 +8165,7 @@
<flag name="hide_from_picker" value="0x2" />
<!-- The widget provides a default configuration. The host may decide not to launch
the provided configuration activity. -->
- <flag name="configuration_optional" value="0x3" />
+ <flag name="configuration_optional" value="0x4" />
</attr>
<!-- A resource identifier for a string containing a short description of the widget. -->
<attr name="description" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cc52655ad7d2..601d66ec7b39 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -829,7 +829,6 @@
{@code FLAG_ACTIVITY_MULTIPLE_TASK} is set.-->
<enum name="singleInstancePerTask" value="4" />
</attr>
-
<!-- Specify the orientation an activity should be run in. If not
specified, it will run in the current preferred orientation
of the screen.
@@ -1603,6 +1602,12 @@
<enum name="sync" value="2" />
</attr>
+ <!-- Attribution tag to be used for permission sub-attribution if a
+ permission is checked in {@link android.content.Context#sendBroadcast(Intent, String)}.
+ Multiple tags can be specified separated by '|'.
+ -->
+ <attr name="attributionTags" format="string" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -2825,6 +2830,10 @@
<p> See {@link android.content.pm.ActivityInfo#FLAG_PREFER_MINIMAL_POST_PROCESSING} -->
<attr name="preferMinimalPostProcessing" format="boolean"/>
+ <!-- Specify the attributionTags to be used if a permission is required due to
+ {@link android.content.Context#sendBroadcast(Intent, String)} being used.
+ Multiple tags can be specified separated by '|'. -->
+ <attr name="attributionTags"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8bc3a52fa17b..f6fee880bf2f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -324,9 +324,6 @@
<item>"0,1"</item>
</string-array>
- <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
- <integer name="config_networkTransitionTimeout">60000</integer>
-
<!-- Whether/how to notify the user on network switches. See LingerMonitor.java. -->
<integer translatable="false" name="config_networkNotifySwitchType">0</integer>
@@ -339,16 +336,6 @@
Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
<integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
- <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
- If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
- and if that value is empty, the framework will use a hard-coded default.
- This is *NOT* a URL that will always be used by the system network validation to detect
- captive portals: NetworkMonitor may use different strategies and will not necessarily use
- this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
- instead. -->
- <!--suppress CheckTagEmptyBody -->
- <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
-
<!-- If the hardware supports specially marking packets that caused a wakeup of the
main CPU, set this value to the mark used. -->
<integer name="config_networkWakeupPacketMark">0</integer>
@@ -456,14 +443,6 @@
apps requested it. -->
<bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
- <!-- Configuration of network interfaces that support WakeOnLAN -->
- <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
- <!--
- <item>wlan0</item>
- <item>eth0</item>
- -->
- </string-array>
-
<!-- This setting is deprecated, please use
com.android.networkstack.tethering.R.array.config_mobile_hotspot_provision_app instead. -->
<string-array translatable="false" name="config_mobile_hotspot_provision_app">
@@ -1961,6 +1940,8 @@
<string name="config_systemWifiCoexManager" translateable="false"></string>
<!-- The name of the package that will hold the wellbeing role. -->
<string name="config_systemWellbeing" translatable="false"></string>
+ <!-- The name of the package that will hold the television notification handler role -->
+ <string name="config_systemTelevisionNotificationHandler" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -2270,6 +2251,9 @@
<bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
<integer name="config_dozeWakeLockScreenDebounce">300</integer>
+ <!-- Type of the quick pickup sensor. Empty if quick pickup is not supported. -->
+ <string name="config_quickPickupSensorType" translatable="false"></string>
+
<!-- Control whether the always on display mode is available. This should only be enabled on
devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
states. -->
@@ -4738,4 +4722,97 @@
<!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
<bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
+
+ <!-- CEC Configuration -->
+ <bool name="config_cecHdmiCecEnabled_userConfigurable">true</bool>
+ <bool name="config_cecHdmiCecControlEnabled_allowed">true</bool>
+ <bool name="config_cecHdmiCecControlEnabled_default">true</bool>
+ <bool name="config_cecHdmiCecControlDisabled_allowed">true</bool>
+ <bool name="config_cecHdmiCecControlDisabled_default">false</bool>
+
+ <bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
+ <bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
+ <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+ <bool name="config_cecHdmiCecVersion20_allowed">true</bool>
+ <bool name="config_cecHdmiCecVersion20_default">false</bool>
+
+ <bool name="config_cecSendStandbyOnSleep_userConfigurable">true</bool>
+ <bool name="config_cecPowerControlModeTv_allowed">true</bool>
+ <bool name="config_cecPowerControlModeTv_default">true</bool>
+ <bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
+ <bool name="config_cecPowerControlModeBroadcast_default">false</bool>
+ <bool name="config_cecPowerControlModeNone_allowed">true</bool>
+ <bool name="config_cecPowerControlModeNone_default">false</bool>
+
+ <bool name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable">true</bool>
+ <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed">true</bool>
+ <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_default">true</bool>
+ <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
+ <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+
+ <bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
+ <bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
+ <bool name="config_cecSystemAudioModeMutingDisabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioModeMutingDisabled_default">false</bool>
+
+ <bool name="config_cecVolumeControlMode_userConfigurable">true</bool>
+ <bool name="config_cecVolumeControlModeEnabled_allowed">true</bool>
+ <bool name="config_cecVolumeControlModeEnabled_default">true</bool>
+ <bool name="config_cecVolumeControlModeDisabled_allowed">true</bool>
+ <bool name="config_cecVolumeControlModeDisabled_default">false</bool>
+
+ <bool name="config_cecTvWakeOnOneTouchPlay_userConfigurable">true</bool>
+ <bool name="config_cecTvWakeOnOneTouchPlayEnabled_allowed">true</bool>
+ <bool name="config_cecTvWakeOnOneTouchPlayEnabled_default">true</bool>
+ <bool name="config_cecTvWakeOnOneTouchPlayDisabled_allowed">true</bool>
+ <bool name="config_cecTvWakeOnOneTouchPlayDisabled_default">false</bool>
+
+ <bool name="config_cecTvSendStandbyOnSleep_userConfigurable">true</bool>
+ <bool name="config_cecTvSendStandbyOnSleepEnabled_allowed">true</bool>
+ <bool name="config_cecTvSendStandbyOnSleepEnabled_default">true</bool>
+ <bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool>
+ <bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool>
+
+ <bool name="config_cecRcProfileTv_userConfigurable">true</bool>
+ <bool name="config_cecRcProfileTvNone_allowed">true</bool>
+ <bool name="config_cecRcProfileTvNone_default">true</bool>
+ <bool name="config_cecRcProfileTvOne_allowed">true</bool>
+ <bool name="config_cecRcProfileTvOne_default">false</bool>
+ <bool name="config_cecRcProfileTvTwo_allowed">true</bool>
+ <bool name="config_cecRcProfileTvTwo_default">false</bool>
+ <bool name="config_cecRcProfileTvThree_allowed">true</bool>
+ <bool name="config_cecRcProfileTvThree_default">false</bool>
+ <bool name="config_cecRcProfileTvFour_allowed">true</bool>
+ <bool name="config_cecRcProfileTvFour_default">false</bool>
+
+ <bool name="config_cecRcProfileSourceRootMenu_userConfigurable">true</bool>
+ <bool name="config_cecRcProfileSourceRootMenuHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceRootMenuHandled_default">true</bool>
+ <bool name="config_cecRcProfileSourceRootMenuNotHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceRootMenuNotHandled_default">false</bool>
+
+ <bool name="config_cecRcProfileSourceSetupMenu_userConfigurable">true</bool>
+ <bool name="config_cecRcProfileSourceSetupMenuHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceSetupMenuHandled_default">true</bool>
+ <bool name="config_cecRcProfileSourceSetupMenuNotHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceSetupMenuNotHandled_default">false</bool>
+
+ <bool name="config_cecRcProfileSourceContentsMenu_userConfigurable">true</bool>
+ <bool name="config_cecRcProfileSourceContentsMenuHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceContentsMenuHandled_default">false</bool>
+ <bool name="config_cecRcProfileSourceContentsMenuNotHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceContentsMenuNotHandled_default">true</bool>
+
+ <bool name="config_cecRcProfileSourceTopMenu_userConfigurable">true</bool>
+ <bool name="config_cecRcProfileSourceTopMenuHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceTopMenuHandled_default">false</bool>
+ <bool name="config_cecRcProfileSourceTopMenuNotHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceTopMenuNotHandled_default">true</bool>
+
+ <bool name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable">true</bool>
+ <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default">false</bool>
+ <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed">true</bool>
+ <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default">true</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 10aa7b3f4354..43dbd38d1dc4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -746,7 +746,7 @@
<dimen name="notification_right_icon_headerless_margin">20dp</dimen>
<!-- The top margin of the right icon in the "big" notification states -->
<!-- TODO(b/181048615): Move the large icon below the expander in big states -->
- <dimen name="notification_right_icon_big_margin_top">20dp</dimen>
+ <dimen name="notification_right_icon_big_margin_top">16dp</dimen>
<!-- The size of the left icon -->
<dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen>
<!-- The left padding of the left icon -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 3a41d5fed238..c3b35c81cb66 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -247,4 +247,13 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER}. -->
<item type="id" name="accessibilityActionImeEnter" />
+
+ <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
+ <item type="id" name="remote_views_next_child" />
+
+ <!-- View tag associating a view with its stable id for potential recycling. -->
+ <item type="id" name="remote_views_stable_id" />
+
+ <!-- View tag associating a view with its overridden id, to ensure valid recycling only. -->
+ <item type="id" name="remote_views_override_id" />
</resources>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/core/res/res/values/policy_exempt_apps.xml
index f32faa0df867..1cead8322e55 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/core/res/res/values/policy_exempt_apps.xml
@@ -14,9 +14,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/udfps_animation_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+<resources>
+ <!--
+ A collection of apps that are critical for the device and hence will never be disabled by
+ device policies or APIs.
+ -->
+ <string-array translatable="false" name="policy_exempt_apps">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 293018d881d8..ba21679df2fe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2816,6 +2816,7 @@
<public type="attr" name="iconSpaceReserved" id="0x01010561"/>
<public type="attr" name="defaultFocusHighlightEnabled" id="0x01010562" />
<public type="attr" name="persistentWhenFeatureAvailable" id="0x01010563"/>
+ <!-- {@deprecated Use windowSplashscreenAnimatedIcon instead } -->
<public type="attr" name="windowSplashscreenContent" id="0x01010564" />
<!-- @hide @SystemApi -->
<public type="attr" name="requiredSystemPropertyName" id="0x01010565" />
@@ -3071,7 +3072,6 @@
<public name="windowSplashScreenAnimationDuration"/>
<public name="windowSplashScreenBrandingImage"/>
<public name="splashScreenTheme" />
- <public name="rippleStyle" />
<public name="maxResizeWidth" />
<public name="maxResizeHeight" />
<public name="targetCellWidth" />
@@ -3088,6 +3088,8 @@
<public name="passwordsActivity"/>
<public name="selectableAsDefault"/>
<public name="isAccessibilityTool"/>
+ <public name="attributionTags"/>
+ <public name="suppressesSpellChecker" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
@@ -3175,6 +3177,8 @@
<public name="config_systemWifiCoexManager" />
<!-- @hide @SystemApi -->
<public name="config_systemWellbeing" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemTelevisionNotificationHandler" />
</public-group>
<public-group type="id" first-id="0x01020055">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2ffa29b53331..0228dfd45972 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -453,6 +453,9 @@
<!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]-->
<string name="gnss_time_update_service">GNSS Time Update Service</string>
+ <!-- Attribution for MusicRecognitionManagerService. [CHAR LIMIT=NONE]-->
+ <string name="music_recognition_manager_service">Music Recognition Manager Service</string>
+
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
<string name="factory_reset_warning">Your device will be erased</string>
@@ -740,6 +743,10 @@
magnification. [CHAR_LIMIT=NONE]-->
<string name="notification_channel_accessibility_magnification">Magnification</string>
+ <!-- Text shown when viewing channel settings for notifications related to accessibility
+ security policy. [CHAR_LIMIT=NONE]-->
+ <string name="notification_channel_accessibility_security_policy">Accessibility security policy</string>
+
<!-- Label for foreground service notification when one app is running.
[CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
<string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is
@@ -889,6 +896,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_expandStatusBar">Allows the app to expand or collapse the status bar.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_fullScreenIntent">display notifications as full screen activities on a locked device</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_fullScreenIntent">Allows the app to display notifications as full screen activities on a locked device</string>
+
<!-- Title of an application permission, listed so the user can install application shortcuts
in their Launcher -->
<string name="permlab_install_shortcut">install shortcuts</string>
@@ -1532,6 +1544,8 @@
<string name="biometric_dialog_default_title">Verify it\u2019s you</string>
<!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with a biometric (e.g. fingerprint or face). [CHAR LIMIT=70] -->
<string name="biometric_dialog_default_subtitle">Use your biometric to continue</string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with a biometric (e.g. fingerprint or face) or their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=90] -->
+ <string name="biometric_or_screen_lock_dialog_default_subtitle">Use your biometric or screen lock to continue</string>
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
@@ -1604,6 +1618,8 @@
<string name="fingerprint_or_screen_lock_app_setting_name">Use fingerprint or screen lock</string>
<!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
<string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint or screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=90] -->
+ <string name="fingerprint_or_screen_lock_dialog_default_subtitle">Use your fingerprint or screen lock to continue</string>
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_error_vendor">
@@ -1704,6 +1720,8 @@
<string name="face_or_screen_lock_app_setting_name">Use face or screen lock</string>
<!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their face. [CHAR LIMIT=70] -->
<string name="face_dialog_default_subtitle">Use face unlock to continue</string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their face or screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=90] -->
+ <string name="face_or_screen_lock_dialog_default_subtitle">Use your face or screen lock to continue</string>
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="face_error_vendor">
@@ -3685,6 +3703,8 @@
<string name="ext_media_checking_notification_title">Checking <xliff:g id="name" example="SD card">%s</xliff:g>\u2026</string>
<!-- Notification body when external media is being checked [CHAR LIMIT=NONE] -->
<string name="ext_media_checking_notification_message">Reviewing current content</string>
+ <!-- TV specific notification body when external media is being checked [CHAR LIMIT=75] -->
+ <string name="ext_media_checking_notification_message" product="tv">Analyzing media storage</string>
<!-- Notification body when new external media is detected [CHAR LIMIT=30] -->
<string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3692,11 +3712,15 @@
<string name="ext_media_new_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
<!-- Notification body when new external media is detected [CHAR LIMIT=NONE] -->
<string name="ext_media_new_notification_message">Tap to set up</string>
+ <!-- TV specific notification body when new external media is detected [CHAR LIMIT=75] -->
+ <string name="ext_media_new_notification_message" product="tv">Select to set up</string>
<!-- Automotive specific notification body when new external media is detected. [CHAR LIMIT=NONE] -->
<string name="ext_media_new_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string>
<!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] -->
<string name="ext_media_ready_notification_message">For transferring photos and media</string>
+ <!-- TV specific notification body when external media is ready for use [CHAR LIMIT=75] -->
+ <string name="ext_media_ready_notification_message" product="tv">Browse media files</string>
<!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] -->
<string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3715,8 +3739,8 @@
<string name="ext_media_unsupported_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
<!-- Notification body when external media is unsupported [CHAR LIMIT=NONE] -->
<string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string>
- <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
- <string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string>
+ <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=75] -->
+ <string name="ext_media_unsupported_notification_message" product="tv">Select to set up <xliff:g id="name" example="SD card">%s</xliff:g> in a supported format.</string>
<!-- Automotive specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
<string name="ext_media_unsupported_notification_message" product="automotive">You may need to reformat the device</string>
@@ -5554,6 +5578,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label">On-screen Accessibility Shortcut Chooser</string>
<!-- Label for triggering hardware accessibility shortcut action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
+ <!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
<!-- Accessibility description of caption view -->
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
@@ -5867,9 +5893,9 @@ ul.</string>
<!-- Window magnification prompt related string. -->
<!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_title">New: Window Magnifier</string>
- <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string>
+ <string name="window_magnification_prompt_title">Magnify part of your screen</string>
+ <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=NONE] -->
+ <string name="window_magnification_prompt_content">You can now magnify your full screen, a specific area, or switch between both options.</string>
<!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
<string name="turn_on_magnification_settings_action">Turn on in Settings</string>
<!-- Notification action to dismiss. [CHAR LIMIT=50] -->
@@ -5888,4 +5914,9 @@ ul.</string>
<string name="splash_screen_view_icon_description">Application icon</string>
<!-- Content description for the branding image on the splash screen. [CHAR LIMIT=50] -->
<string name="splash_screen_view_branding_description">Application branding image</string>
+
+ <!-- Notification title to prompt the user that some accessibility service has view and control access. [CHAR LIMIT=50] -->
+ <string name="view_and_control_notification_title">Check access settings</string>
+ <!-- Notification content to prompt the user that some accessibility service has view and control access. [CHAR LIMIT=none] -->
+ <string name="view_and_control_notification_content"><xliff:g id="service_name" example="TalkBack">%s</xliff:g> can view and control your screen. Tap to review.</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 07938fd3a324..567feee31673 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -692,7 +692,6 @@
<java-symbol type="string" name="not_checked" />
<java-symbol type="array" name="config_ethernet_interfaces" />
<java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" />
- <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1266,6 +1265,8 @@
<java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
<java-symbol type="array" name="cross_profile_apps" />
<java-symbol type="array" name="vendor_cross_profile_apps" />
+ <java-symbol type="array" name="policy_exempt_apps" />
+ <java-symbol type="array" name="vendor_policy_exempt_apps" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -1970,11 +1971,9 @@
<java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
<java-symbol type="integer" name="config_lowBatteryWarningLevel" />
<java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
- <java-symbol type="integer" name="config_networkTransitionTimeout" />
<java-symbol type="integer" name="config_networkNotifySwitchType" />
<java-symbol type="array" name="config_networkNotifySwitches" />
<java-symbol type="integer" name="config_networkAvoidBadWifi" />
- <java-symbol type="string" name="config_networkCaptivePortalServerUrl" />
<java-symbol type="integer" name="config_networkWakeupPacketMark" />
<java-symbol type="integer" name="config_networkWakeupPacketMask" />
<java-symbol type="bool" name="config_apfDrop802_3Frames" />
@@ -2473,6 +2472,7 @@
<java-symbol type="string" name="biometric_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="biometric_dialog_default_title" />
<java-symbol type="string" name="biometric_dialog_default_subtitle" />
+ <java-symbol type="string" name="biometric_or_screen_lock_dialog_default_subtitle" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
<java-symbol type="string" name="biometric_error_user_canceled" />
<java-symbol type="string" name="biometric_not_recognized" />
@@ -2504,6 +2504,7 @@
<java-symbol type="string" name="fingerprint_app_setting_name" />
<java-symbol type="string" name="fingerprint_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
+ <java-symbol type="string" name="fingerprint_or_screen_lock_dialog_default_subtitle" />
<java-symbol type="string" name="fingerprint_authenticated" />
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
<java-symbol type="string" name="fingerprint_error_hw_not_present" />
@@ -2553,6 +2554,7 @@
<java-symbol type="string" name="face_app_setting_name" />
<java-symbol type="string" name="face_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="face_dialog_default_subtitle" />
+ <java-symbol type="string" name="face_or_screen_lock_dialog_default_subtitle" />
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
<java-symbol type="string" name="face_error_security_update_required" />
@@ -3113,6 +3115,7 @@
<!-- Notifications: CallStyle -->
<java-symbol type="layout" name="notification_template_material_call" />
+ <java-symbol type="layout" name="notification_template_material_big_call" />
<java-symbol type="string" name="call_notification_answer_action" />
<java-symbol type="string" name="call_notification_decline_action" />
<java-symbol type="string" name="call_notification_hang_up_action" />
@@ -3538,6 +3541,7 @@
<java-symbol type="string" name="notification_channel_system_changes" />
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="notification_channel_accessibility_magnification" />
+ <java-symbol type="string" name="notification_channel_accessibility_security_policy" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
@@ -3592,6 +3596,7 @@
<java-symbol type="string" name="config_dozeUdfpsLongPressSensorType" />
<java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" />
<java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" />
+ <java-symbol type="string" name="config_quickPickupSensorType" />
<java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
@@ -3983,6 +3988,7 @@
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_label" />
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
<java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
+ <java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
<java-symbol type="string" name="accessibility_freeform_caption" />
@@ -4221,4 +4227,106 @@
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
<java-symbol type="bool" name="config_enableOneHandedKeyguard" />
+
+ <!-- CEC Configuration -->
+ <java-symbol type="bool" name="config_cecHdmiCecEnabled_userConfigurable" />
+ <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_default" />
+
+ <java-symbol type="bool" name="config_cecHdmiCecVersion_userConfigurable" />
+ <java-symbol type="bool" name="config_cecHdmiCecVersion14b_allowed" />
+ <java-symbol type="bool" name="config_cecHdmiCecVersion14b_default" />
+ <java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
+ <java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
+
+ <java-symbol type="bool" name="config_cecSendStandbyOnSleep_userConfigurable" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+ <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
+ <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
+ <java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
+ <java-symbol type="bool" name="config_cecPowerControlModeNone_default" />
+
+ <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable" />
+ <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed" />
+ <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_default" />
+ <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
+ <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
+
+ <java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
+ <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_default" />
+
+ <java-symbol type="bool" name="config_cecVolumeControlMode_userConfigurable" />
+ <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_default" />
+ <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_default" />
+
+ <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlay_userConfigurable" />
+ <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_default" />
+ <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_default" />
+
+ <java-symbol type="bool" name="config_cecTvSendStandbyOnSleep_userConfigurable" />
+ <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_default" />
+ <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" />
+
+ <java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileTvNone_default" />
+ <java-symbol type="bool" name="config_cecRcProfileTvOne_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileTvOne_default" />
+ <java-symbol type="bool" name="config_cecRcProfileTvTwo_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileTvTwo_default" />
+ <java-symbol type="bool" name="config_cecRcProfileTvThree_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileTvThree_default" />
+ <java-symbol type="bool" name="config_cecRcProfileTvFour_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileTvFour_default" />
+
+ <java-symbol type="bool" name="config_cecRcProfileSourceRootMenu_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_default" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_default" />
+
+ <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenu_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_default" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_default" />
+
+ <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenu_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_default" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_default" />
+
+ <java-symbol type="bool" name="config_cecRcProfileSourceTopMenu_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_default" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_default" />
+
+ <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed" />
+ <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default" />
+
+ <java-symbol type="id" name="remote_views_next_child" />
+ <java-symbol type="id" name="remote_views_stable_id" />
+ <java-symbol type="id" name="remote_views_override_id" />
+
+ <!-- View and control prompt -->
+ <java-symbol type="drawable" name="ic_accessibility_24dp" />
+ <java-symbol type="string" name="view_and_control_notification_title" />
+ <java-symbol type="string" name="view_and_control_notification_content" />
</resources>
diff --git a/core/res/res/values/vendor_policy_exempt_apps.xml b/core/res/res/values/vendor_policy_exempt_apps.xml
new file mode 100644
index 000000000000..eb4c760b6724
--- /dev/null
+++ b/core/res/res/values/vendor_policy_exempt_apps.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!--
+ A collection of apps that are critical for the device and hence will never be disabled by
+ device policies or APIs.
+ -->
+ <string-array translatable="false" name="vendor_policy_exempt_apps">
+ </string-array>
+</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
new file mode 100644
index 000000000000..7ef1d5e426cc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+public class AppSearchSessionUnitTest {
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final AppSearchManager mAppSearch = mContext.getSystemService(AppSearchManager.class);
+ private final Executor mExecutor = mContext.getMainExecutor();
+ private AppSearchSession mSearchSession;
+
+ @Before
+ public void setUp() throws Exception {
+ // Remove all documents from any instances that may have been created in the tests.
+ Objects.requireNonNull(mAppSearch);
+ AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder()
+ .setDatabaseName("testDb").build();
+ CompletableFuture<AppSearchResult<AppSearchSession>> future = new CompletableFuture<>();
+ mAppSearch.createSearchSession(searchContext, mExecutor, future::complete);
+ mSearchSession = future.get().getResultValue();
+
+ CompletableFuture<AppSearchResult<SetSchemaResponse>> schemaFuture =
+ new CompletableFuture<>();
+ mSearchSession.setSchema(
+ new SetSchemaRequest.Builder().setForceOverride(true).build(), mExecutor,
+ schemaFuture::complete);
+
+ schemaFuture.get().getResultValue();
+ }
+
+ @Test
+ public void testPutDocument_throwsNullException() throws Exception {
+ // Create a document
+ AppSearchEmail inEmail =
+ new AppSearchEmail.Builder("namespace", "uri1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+
+ // clear the document bundle to make our service crash and throw an NullPointerException.
+ inEmail.getBundle().clear();
+ CompletableFuture<AppSearchBatchResult<String, Void>> putDocumentsFuture =
+ new CompletableFuture<>();
+
+ // Index the broken document.
+ mSearchSession.put(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail).build(),
+ mExecutor, new BatchResultCallback<String, Void>() {
+ @Override
+ public void onResult(AppSearchBatchResult<String, Void> result) {
+ putDocumentsFuture.complete(result);
+ }
+
+ @Override
+ public void onSystemError(Throwable throwable) {
+ putDocumentsFuture.completeExceptionally(throwable);
+ }
+ });
+
+ // Verify the NullPointException has been thrown.
+ ExecutionException executionException = expectThrows(ExecutionException.class,
+ putDocumentsFuture::get);
+ assertThat(executionException.getCause()).isInstanceOf(NullPointerException.class);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
index 119b70ab0439..ed53d5f39a39 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
@@ -25,7 +25,7 @@ public class AppSearchEmailTest {
@Test
public void testBuildEmailAndGetValue() {
AppSearchEmail email =
- new AppSearchEmail.Builder("uri")
+ new AppSearchEmail.Builder("namespace", "uri")
.setFrom("FakeFromAddress")
.setCc("CC1", "CC2")
// Score and Property are mixed into the middle to make sure
@@ -37,6 +37,7 @@ public class AppSearchEmailTest {
.setBody("EmailBody")
.build();
+ assertThat(email.getNamespace()).isEqualTo("namespace");
assertThat(email.getUri()).isEqualTo("uri");
assertThat(email.getFrom()).isEqualTo("FakeFromAddress");
assertThat(email.getTo()).isNull();
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index af77b6c598b1..b884ddcd1420 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -27,13 +27,13 @@ public class GenericDocumentTest {
@Test
public void testRecreateFromParcel() {
GenericDocument inDoc =
- new GenericDocument.Builder<>("uri1", "schema1")
+ new GenericDocument.Builder<>("namespace", "uri1", "schema1")
.setScore(42)
.setPropertyString("propString", "Hello")
.setPropertyBytes("propBytes", new byte[][] {{1, 2}})
.setPropertyDocument(
"propDocument",
- new GenericDocument.Builder<>("uri2", "schema2")
+ new GenericDocument.Builder<>("namespace", "uri2", "schema2")
.setPropertyString("propString", "Goodbye")
.setPropertyBytes("propBytes", new byte[][] {{3, 4}})
.build())
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
index 7d175d9b31e5..76372141ca04 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
@@ -31,8 +31,8 @@ public class PutDocumentsRequestTest {
public void addGenericDocument_byCollection() {
Set<AppSearchEmail> emails =
ImmutableSet.of(
- new AppSearchEmail.Builder("test1").build(),
- new AppSearchEmail.Builder("test2").build());
+ new AppSearchEmail.Builder("namespace", "test1").build(),
+ new AppSearchEmail.Builder("namespace", "test2").build());
PutDocumentsRequest request =
new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 6c8b94129c82..9915e3852b8d 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -133,7 +133,7 @@ public class ObjectPoolTests {
int ident = 57;
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.flags = 42;
- activityInfo.maxAspectRatio = 2.4f;
+ activityInfo.setMaxAspectRatio(2.4f);
activityInfo.launchToken = "token";
activityInfo.applicationInfo = new ApplicationInfo();
activityInfo.packageName = "packageName";
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 6f3d7ae5eb3c..f47fa39e7dc8 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -182,7 +182,7 @@ public class TransactionParcelTests {
int ident = 57;
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.flags = 42;
- activityInfo.maxAspectRatio = 2.4f;
+ activityInfo.setMaxAspectRatio(2.4f);
activityInfo.launchToken = "token";
activityInfo.applicationInfo = new ApplicationInfo();
activityInfo.packageName = "packageName";
diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
new file mode 100644
index 000000000000..85073b0ecfdd
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+
+import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TimeCapabilitiesTest {
+
+ private static final UserHandle USER_HANDLE = UserHandle.of(332211);
+
+ @Test
+ public void testBuilder() {
+ TimeCapabilities capabilities = new TimeCapabilities.Builder(USER_HANDLE)
+ .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE)
+ .setSuggestTimeManuallyCapability(CAPABILITY_NOT_SUPPORTED)
+ .build();
+
+ assertThat(capabilities.getConfigureAutoTimeDetectionEnabledCapability())
+ .isEqualTo(CAPABILITY_NOT_APPLICABLE);
+ assertThat(capabilities.getSuggestTimeManuallyCapability())
+ .isEqualTo(CAPABILITY_NOT_SUPPORTED);
+
+ try {
+ new TimeCapabilities.Builder(USER_HANDLE)
+ .build();
+ fail("Should throw IllegalStateException");
+ } catch (IllegalStateException ignored) {
+ // expected
+ }
+
+ try {
+ new TimeCapabilities.Builder(USER_HANDLE)
+ .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE)
+ .build();
+ fail("Should throw IllegalStateException");
+ } catch (IllegalStateException ignored) {
+ // expected
+ }
+
+ try {
+ new TimeCapabilities.Builder(USER_HANDLE)
+ .setSuggestTimeManuallyCapability(CAPABILITY_NOT_APPLICABLE)
+ .build();
+ fail("Should throw IllegalStateException");
+ } catch (IllegalStateException ignored) {
+ // expected
+ }
+ }
+
+ @Test
+ public void userHandle_notIgnoredInEquals() {
+ TimeCapabilities firstUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(1))
+ .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED)
+ .setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED)
+ .build();
+
+ TimeCapabilities secondUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(2))
+ .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED)
+ .setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED)
+ .build();
+
+ assertThat(firstUserCapabilities).isNotEqualTo(secondUserCapabilities);
+ }
+
+ @Test
+ public void testParcelable() {
+ TimeCapabilities.Builder builder = new TimeCapabilities.Builder(USER_HANDLE)
+ .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_SUPPORTED)
+ .setSuggestTimeManuallyCapability(CAPABILITY_NOT_SUPPORTED);
+
+ assertRoundTripParcelable(builder.build());
+
+ builder.setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED);
+ assertRoundTripParcelable(builder.build());
+
+ builder.setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED);
+ assertRoundTripParcelable(builder.build());
+ }
+
+}
diff --git a/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java b/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java
new file mode 100644
index 000000000000..7c7cd12bcb73
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TimeConfigurationTest {
+
+ @Test
+ public void testBuilder() {
+ TimeConfiguration first = new TimeConfiguration.Builder()
+ .setAutoDetectionEnabled(true)
+ .build();
+
+ assertThat(first.isAutoDetectionEnabled()).isTrue();
+
+ TimeConfiguration copyFromBuilderConfiguration = new TimeConfiguration.Builder(first)
+ .build();
+
+ assertThat(first).isEqualTo(copyFromBuilderConfiguration);
+ }
+
+ @Test
+ public void testParcelable() {
+ TimeConfiguration.Builder builder = new TimeConfiguration.Builder();
+
+ assertRoundTripParcelable(builder.setAutoDetectionEnabled(true).build());
+
+ assertRoundTripParcelable(builder.setAutoDetectionEnabled(false).build());
+ }
+
+}
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
index 01a25b27baf6..dd93997b00c5 100644
--- a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
@@ -16,8 +16,8 @@
package android.app.time;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static org.junit.Assert.assertEquals;
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 8de9454ddeda..083e37a3aaa6 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -46,7 +46,8 @@ public class UsageStatsPersistenceTest {
private static final String[] USAGESTATS_PERSISTED_FIELDS = {"mBeginTimeStamp", "mEndTimeStamp",
"mPackageName", "mPackageToken", "mLastEvent", "mAppLaunchCount", "mChooserCounts",
"mLastTimeUsed", "mTotalTimeInForeground", "mLastTimeForegroundServiceUsed",
- "mTotalTimeForegroundServiceUsed", "mLastTimeVisible", "mTotalTimeVisible"};
+ "mTotalTimeForegroundServiceUsed", "mLastTimeVisible", "mTotalTimeVisible",
+ "mLastTimeComponentUsed"};
// All fields in this list are defined in UsageStats but not persisted
private static final String[] USAGESTATS_IGNORED_FIELDS = {"CREATOR", "mActivities",
"mForegroundServices", "mLaunchCount", "mChooserCountsObfuscated"};
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 0ac00b8e9bbc..858bbd20f13d 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -20,6 +20,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.END_OF_DAY;
@@ -137,6 +138,7 @@ public class UsageStatsTest {
left.mPackageName = "com.test";
left.mBeginTimeStamp = 100000;
left.mTotalTimeInForeground = 10;
+ left.mLastTimeComponentUsed = 200000;
left.mActivities.put(1, Event.ACTIVITY_RESUMED);
left.mActivities.put(2, Event.ACTIVITY_RESUMED);
@@ -542,6 +544,19 @@ public class UsageStatsTest {
}
@Test
+ public void testEvent_APP_COMPONENT_USED() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+ final String className = "com.test.component1";
+
+ left.update(className, 200000, APP_COMPONENT_USED, 0);
+ assertEquals(left.mLastTimeComponentUsed, 200000);
+
+ left.update(className, 300000, APP_COMPONENT_USED, 0);
+ assertEquals(left.mLastTimeComponentUsed, 300000);
+ }
+
+ @Test
public void testEvent_DEVICE_SHUTDOWN() {
testClosingEvent(DEVICE_SHUTDOWN);
}
@@ -586,6 +601,7 @@ public class UsageStatsTest {
assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp);
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
+ assertEquals(us1.mLastTimeComponentUsed, us2.mLastTimeComponentUsed);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index 8895f9bdf23d..b28206402137 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.withSettings;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.UserHandle;
import androidx.test.runner.AndroidJUnit4;
@@ -86,4 +87,11 @@ public class ContentProviderTest {
mCp.validateIncomingUri(
Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar")));
}
+
+ @Test
+ public void testCreateContentUriAsUser() {
+ Uri uri = Uri.parse("content://com.example/foo/bar");
+ Uri expectedUri = Uri.parse("content://7@com.example/foo/bar");
+ assertEquals(expectedUri, ContentProvider.createContentUriAsUser(uri, UserHandle.of(7)));
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 57c0be5007b8..5d75d9b3f70b 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -2923,7 +2923,7 @@ public class PackageManagerTests extends AndroidTestCase {
assertFalse("BaseCodePath should not be registered", callback.mSuccess);
}
- // Verify thatmodules which are not own by the calling package are not registered.
+ // Verify that modules which are not own by the calling package are not registered.
public void testRegisterDexModuleNotOwningModule() throws Exception {
TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback();
String moduleBelongingToOtherPackage = "/data/user/0/com.google.android.gms/module.apk";
@@ -2978,6 +2978,13 @@ public class PackageManagerTests extends AndroidTestCase {
assertFalse("DexModule registration should fail", callback.mSuccess);
}
+ // If the module does not exist on disk we should get a failure.
+ public void testRegisterDexModuleNotExistsNoCallback() throws Exception {
+ ApplicationInfo info = getContext().getApplicationInfo();
+ String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString();
+ getPm().registerDexModule(nonExistentApk, null);
+ }
+
// Copied from com.android.server.pm.InstructionSets because we don't have access to it here.
private static String[] getAppDexInstructionSets(ApplicationInfo info) {
if (info.primaryCpuAbi != null) {
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index c06405affc1b..09c36dd261bd 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -16,9 +16,10 @@
package android.os;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
@@ -33,72 +34,128 @@ public class VibratorInfoTest {
@Test
public void testHasAmplitudeControl() {
- assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl());
- assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS
- | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl());
+ VibratorInfo noCapabilities = new InfoBuilder().build();
+ assertFalse(noCapabilities.hasAmplitudeControl());
+ VibratorInfo composeAndAmplitudeControl = new InfoBuilder()
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
+ | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
+ assertTrue(composeAndAmplitudeControl.hasAmplitudeControl());
}
@Test
public void testHasCapabilities() {
- assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
- .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
- assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
- .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
+ VibratorInfo info = new InfoBuilder()
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .build();
+ assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
+ assertFalse(info.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
}
@Test
public void testIsEffectSupported() {
- VibratorInfo info = new VibratorInfo(/* id= */ 0, /* capabilities= */0,
- new int[]{VibrationEffect.EFFECT_CLICK}, null);
+ VibratorInfo noEffects = new InfoBuilder().build();
+ VibratorInfo canClick = new InfoBuilder()
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
- createInfo(/* capabilities= */ 0).isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ noEffects.isEffectSupported(VibrationEffect.EFFECT_CLICK));
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ canClick.isEffectSupported(VibrationEffect.EFFECT_CLICK));
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
- info.isEffectSupported(VibrationEffect.EFFECT_TICK));
+ canClick.isEffectSupported(VibrationEffect.EFFECT_TICK));
}
@Test
public void testIsPrimitiveSupported() {
- VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS,
- null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+ VibratorInfo info = new InfoBuilder()
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .build();
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
// Returns false when there is no compose capability.
- info = new VibratorInfo(/* id= */ 0, /* capabilities= */ 0,
- null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+ info = new InfoBuilder()
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .build();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@Test
public void testEquals() {
- VibratorInfo empty = new VibratorInfo(1, 0, null, null);
- VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{VibrationEffect.EFFECT_CLICK},
- new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+ InfoBuilder completeBuilder = new InfoBuilder()
+ .setId(1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setQFactor(2f)
+ .setResonantFrequency(150f);
+ VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
- assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{VibrationEffect.EFFECT_CLICK},
- new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}));
-
- assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
- new int[]{VibrationEffect.EFFECT_CLICK},
- new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{VibrationEffect.EFFECT_CLICK}, null)));
+ assertEquals(complete, completeBuilder.build());
+
+ VibratorInfo completeWithComposeControl = completeBuilder
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .build();
+ assertNotEquals(complete, completeWithComposeControl);
+
+ VibratorInfo completeWithNoEffects = completeBuilder
+ .setSupportedEffects()
+ .setSupportedPrimitives()
+ .build();
+ assertNotEquals(complete, completeWithNoEffects);
+
+ VibratorInfo completeWithUnknownEffects = completeBuilder
+ .setSupportedEffects(null)
+ .build();
+ assertNotEquals(complete, completeWithNoEffects);
+
+ VibratorInfo completeWithUnknownPrimitives = completeBuilder
+ .setSupportedPrimitives(null)
+ .build();
+ assertNotEquals(complete, completeWithUnknownPrimitives);
+
+ VibratorInfo completeWithDifferentF0 = completeBuilder
+ .setResonantFrequency(complete.getResonantFrequency() + 3f)
+ .build();
+ assertNotEquals(complete, completeWithDifferentF0);
+
+ VibratorInfo completeWithUnknownF0 = completeBuilder
+ .setResonantFrequency(Float.NaN)
+ .build();
+ assertNotEquals(complete, completeWithUnknownF0);
+
+ VibratorInfo completeWithUnknownQFactor = completeBuilder
+ .setQFactor(Float.NaN)
+ .build();
+ assertNotEquals(complete, completeWithUnknownQFactor);
+
+ VibratorInfo completeWithDifferentQFactor = completeBuilder
+ .setQFactor(complete.getQFactor() + 3f)
+ .build();
+ assertNotEquals(complete, completeWithDifferentQFactor);
+
+ VibratorInfo empty = new InfoBuilder().setId(1).build();
+ VibratorInfo emptyWithKnownSupport = new InfoBuilder()
+ .setId(1)
+ .setSupportedEffects()
+ .setSupportedPrimitives()
+ .build();
+ assertNotEquals(empty, emptyWithKnownSupport);
}
@Test
- public void testSerialization() {
- VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
- new int[]{VibrationEffect.EFFECT_CLICK}, null);
+ public void testParceling() {
+ VibratorInfo original = new InfoBuilder()
+ .setId(1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .setSupportedPrimitives(null)
+ .setResonantFrequency(1.3f)
+ .setQFactor(Float.NaN)
+ .build();
Parcel parcel = Parcel.obtain();
original.writeToParcel(parcel, 0);
@@ -107,7 +164,47 @@ public class VibratorInfoTest {
assertEquals(original, restored);
}
- private static VibratorInfo createInfo(long capabilities) {
- return new VibratorInfo(/* id= */ 0, capabilities, null, null);
+ private static class InfoBuilder {
+ private int mId = 0;
+ private int mCapabilities = 0;
+ private int[] mSupportedEffects = null;
+ private int[] mSupportedPrimitives = null;
+ private float mResonantFrequency = Float.NaN;
+ private float mQFactor = Float.NaN;
+
+ public InfoBuilder setId(int id) {
+ mId = id;
+ return this;
+ }
+
+ public InfoBuilder setCapabilities(int capabilities) {
+ mCapabilities = capabilities;
+ return this;
+ }
+
+ public InfoBuilder setSupportedEffects(int... supportedEffects) {
+ mSupportedEffects = supportedEffects;
+ return this;
+ }
+
+ public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) {
+ mSupportedPrimitives = supportedPrimitives;
+ return this;
+ }
+
+ public InfoBuilder setResonantFrequency(float resonantFrequency) {
+ mResonantFrequency = resonantFrequency;
+ return this;
+ }
+
+ public InfoBuilder setQFactor(float qFactor) {
+ mQFactor = qFactor;
+ return this;
+ }
+
+ public VibratorInfo build() {
+ return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
+ mResonantFrequency, mQFactor);
+ }
}
}
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 80165f065995..fa1aa5eab26c 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -9,3 +9,6 @@ per-file *Focus* = file:/services/core/java/com/android/server/wm/OWNERS
per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
index 8eb13bc03b4b..43490213c29a 100644
--- a/core/tests/coretests/src/android/view/RoundedCornerTest.java
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -62,6 +62,13 @@ public class RoundedCornerTest {
}
@Test
+ public void testIsEmpty_negativeCenter() {
+ RoundedCorner roundedCorner =
+ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT, 1, -2, -3);
+ assertThat(roundedCorner.isEmpty(), is(true));
+ }
+
+ @Test
public void testEquals() {
RoundedCorner roundedCorner = new RoundedCorner(
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index 516fb76eeaf7..f3a6f9e9de17 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -74,7 +74,7 @@ public class ScrollCaptureConnectionTest {
mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback);
mTarget.setScrollBounds(mScrollBounds);
- mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
+ mConnection = new ScrollCaptureConnection(Runnable::run, mTarget);
}
/** Test creating a client with valid info */
@@ -83,7 +83,7 @@ public class ScrollCaptureConnectionTest {
ScrollCaptureTarget target = new ScrollCaptureTarget(
mView, mLocalVisibleRect, mPositionInWindow, mCallback);
target.setScrollBounds(new Rect(1, 2, 3, 4));
- new ScrollCaptureConnection(Runnable::run, target, mRemote);
+ new ScrollCaptureConnection(Runnable::run, target);
}
/** Test creating a client fails if arguments are not valid. */
@@ -91,20 +91,20 @@ public class ScrollCaptureConnectionTest {
public void testConstruction_requiresScrollBounds() {
try {
mTarget.setScrollBounds(null);
- new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
+ new ScrollCaptureConnection(Runnable::run, mTarget);
fail("An exception was expected.");
} catch (RuntimeException ex) {
// Ignore, expected.
}
}
- /** @see ScrollCaptureConnection#startCapture(Surface) */
+ /** @see ScrollCaptureConnection#startCapture(Surface, IScrollCaptureCallbacks) */
@Test
public void testStartCapture() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
- assertTrue(mConnection.isStarted());
+ assertTrue(mConnection.isActive());
verify(mRemote, times(1)).onCaptureStarted();
verifyNoMoreInteractions(mRemote);
@@ -112,11 +112,11 @@ public class ScrollCaptureConnectionTest {
@Test
public void testStartCapture_cancellation() throws Exception {
- ICancellationSignal signal = mConnection.startCapture(mSurface);
+ ICancellationSignal signal = mConnection.startCapture(mSurface, mRemote);
signal.cancel();
mCallback.completeStartRequest();
- assertFalse(mConnection.isStarted());
+ assertFalse(mConnection.isActive());
verifyNoMoreInteractions(mRemote);
}
@@ -124,7 +124,7 @@ public class ScrollCaptureConnectionTest {
/** @see ScrollCaptureConnection#requestImage(Rect) */
@Test
public void testRequestImage() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -138,7 +138,7 @@ public class ScrollCaptureConnectionTest {
@Test
public void testRequestImage_cancellation() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -152,7 +152,7 @@ public class ScrollCaptureConnectionTest {
/** @see ScrollCaptureConnection#endCapture() */
@Test
public void testEndCapture() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -167,7 +167,7 @@ public class ScrollCaptureConnectionTest {
/** @see ScrollCaptureConnection#endCapture() */
@Test
public void testEndCapture_cancellation() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -179,9 +179,9 @@ public class ScrollCaptureConnectionTest {
}
@Test
- public void testClose() throws Exception {
+ public void testClose() {
mConnection.close();
- assertFalse(mConnection.isConnected());
+ assertFalse(mConnection.isActive());
verifyNoMoreInteractions(mRemote);
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 7746bc2e273a..e0d9ecfcb336 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -205,7 +205,7 @@ public class ViewRootImplTest {
@Test
public void requestScrollCapture_withoutContentRoot() {
final CountDownLatch latch = new CountDownLatch(1);
- mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+ mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() {
@Override
public void onScrollCaptureResponse(ScrollCaptureResponse response) {
latch.countDown();
@@ -237,7 +237,7 @@ public class ViewRootImplTest {
final CountDownLatch latch = new CountDownLatch(1);
mViewRootImpl.setScrollCaptureRequestTimeout(100);
- mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+ mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() {
@Override
public void onScrollCaptureResponse(ScrollCaptureResponse response) {
latch.countDown();
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
index 1e0e1235161c..f5fcb03bb816 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
@@ -125,7 +125,7 @@ public class InputMethodSubtypeTest {
assertEquals("he", clonedSubtypeHe.getLocale());
}
- private static final InputMethodSubtype cloneViaParcel(final InputMethodSubtype original) {
+ private static InputMethodSubtype cloneViaParcel(final InputMethodSubtype original) {
Parcel parcel = null;
try {
parcel = Parcel.obtain();
@@ -157,4 +157,4 @@ public class InputMethodSubtypeTest {
.setIsAsciiCapable(true)
.build();
}
-} \ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
index 453ad72b64dc..f264cc630dc5 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
@@ -62,20 +62,20 @@ public class SparseRectFArrayTest {
@Test
public void testBuilder() throws Exception {
- final RectF TEMP_RECT = new RectF(10.0f, 20.0f, 30.0f, 40.0f);
- final int TEMP_FLAGS = 0x1234;
+ final RectF testRect = new RectF(10.0f, 20.0f, 30.0f, 40.0f);
+ final int testFlags = 0x1234;
final SparseRectFArrayBuilder builder = new SparseRectFArrayBuilder();
- builder.append(100, TEMP_RECT.left, TEMP_RECT.top, TEMP_RECT.right, TEMP_RECT.bottom,
- TEMP_FLAGS);
+ builder.append(100, testRect.left, testRect.top, testRect.right, testRect.bottom,
+ testFlags);
assertNull(builder.build().get(-1));
assertNull(builder.build().get(0));
assertNull(builder.build().get(99));
assertEquals(0, builder.build().getFlags(99, 0 /* valueIfKeyNotFound */));
assertEquals(1, builder.build().getFlags(99, 1 /* valueIfKeyNotFound */));
- assertEquals(TEMP_RECT, builder.build().get(100));
- assertEquals(TEMP_FLAGS, builder.build().getFlags(100, 0 /* valueIfKeyNotFound */));
- assertEquals(TEMP_FLAGS, builder.build().getFlags(100, 1 /* valueIfKeyNotFound */));
+ assertEquals(testRect, builder.build().get(100));
+ assertEquals(testFlags, builder.build().getFlags(100, 0 /* valueIfKeyNotFound */));
+ assertEquals(testFlags, builder.build().getFlags(100, 1 /* valueIfKeyNotFound */));
assertNull(builder.build().get(101));
assertEquals(0, builder.build().getFlags(101, 0 /* valueIfKeyNotFound */));
assertEquals(1, builder.build().getFlags(101, 1 /* valueIfKeyNotFound */));
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index f6e02bc1f48a..28f9ccc10135 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -41,7 +41,9 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import android.content.res.TypedArray;
import android.text.Selection;
@@ -356,4 +358,71 @@ public class SuggestionsPopupWindowTest {
.perform(clearText());
}
}
+
+ @Test
+ public void testCursorVisibility() {
+ final TextView textView = getActivity().findViewById(R.id.textview);
+ final String text = "abc";
+
+ assertTrue(textView.isCursorVisible());
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1);
+ showSuggestionsPopup();
+
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("ABC");
+ assertFalse(textView.isCursorVisible());
+
+ // Delete an item.
+ clickSuggestionsPopupItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+ assertSuggestionsPopupIsNotDisplayed();
+ assertTrue(textView.isCursorVisible());
+ }
+
+ @Test
+ public void testCursorVisibilityWhenImeConsumesInput() {
+ final TextView textView = getActivity().findViewById(R.id.textview);
+ final String text = "abc";
+
+ assertTrue(textView.isCursorVisible());
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ setImeConsumesInputWithExpect(textView, true /* imeConsumesInput */,
+ false /* expectedCursorVisibility */);
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1);
+ showSuggestionsPopup();
+
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("ABC");
+ assertFalse(textView.isCursorVisible());
+
+ // Delete an item.
+ clickSuggestionsPopupItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+ assertSuggestionsPopupIsNotDisplayed();
+ assertFalse(textView.isCursorVisible());
+
+ // Set IME not consumes input, cursor should be back to visible.
+ setImeConsumesInputWithExpect(textView, false /* imeConsumesInput */,
+ true /* expectedCursorVisibility */);
+ }
+
+ private void setImeConsumesInputWithExpect(
+ final TextView textView, boolean imeConsumesInput, boolean expectedCursorVisibility) {
+ textView.post(() -> textView.setImeConsumesInput(imeConsumesInput));
+ getInstrumentation().waitForIdleSync();
+ if (expectedCursorVisibility) {
+ assertTrue(textView.isCursorVisible());
+ } else {
+ assertFalse(textView.isCursorVisible());
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index ee472880b79f..46e2772b30ca 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -60,6 +60,7 @@ import org.junit.runners.Suite;
KernelCpuUidFreqTimeReaderTest.class,
KernelCpuUidUserSysTimeReaderTest.class,
KernelMemoryBandwidthStatsTest.class,
+ KernelSingleProcessCpuThreadReaderTest.class,
KernelSingleUidTimeReaderTest.class,
KernelWakelockReaderTest.class,
LongSamplingCounterTest.class,
@@ -69,6 +70,7 @@ import org.junit.runners.Suite;
PowerProfileTest.class,
ScreenPowerCalculatorTest.class,
SensorPowerCalculatorTest.class,
+ SystemServerCpuThreadReaderTest.class,
SystemServicePowerCalculatorTest.class,
UserPowerCalculatorTest.class,
VideoPowerCalculatorTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 8aeb761ffc4d..80ab36ec84cf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -33,11 +33,15 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.power.MeasuredEnergyStats;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.stubbing.Answer;
+import java.util.Arrays;
+
public class BatteryUsageStatsRule implements TestRule {
private final PowerProfile mPowerProfile;
private final MockClocks mMockClocks = new MockClocks();
@@ -98,6 +102,16 @@ public class BatteryUsageStatsRule implements TestRule {
return this;
}
+ /** Call only after setting the power profile information. */
+ public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(int numCustom) {
+ final boolean[] supportedStandardBuckets =
+ new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, numCustom);
+ mBatteryStats.informThatAllExternalStatsAreFlushed();
+ return this;
+ }
+
public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
mScreenOn = screenOn;
return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 23ea508d19d3..33b8aedb7970 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -35,7 +35,6 @@ import org.junit.runner.RunWith;
import java.util.List;
@SmallTest
-@SkipPresubmit("b/180015146")
@RunWith(AndroidJUnit4.class)
public class BatteryUsageStatsTest {
@@ -102,7 +101,7 @@ public class BatteryUsageStatsTest {
}
public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
- assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100);
+ assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(21500);
assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
@@ -128,7 +127,7 @@ public class BatteryUsageStatsTest {
BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710);
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
@@ -146,7 +145,7 @@ public class BatteryUsageStatsTest {
BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
- assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510);
+ assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300);
} else {
fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index f6aa08bf0645..71d7668bbcfa 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.annotation.Nullable;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.SystemBatteryConsumer;
@@ -43,7 +44,6 @@ public class BluetoothPowerCalculatorTest {
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0);
@Test
- @SkipPresubmit("b/180015146")
public void testTimerBasedModel() {
setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
.getOrCreateBluetoothControllerActivityLocked(),
@@ -60,11 +60,10 @@ public class BluetoothPowerCalculatorTest {
BluetoothPowerCalculator calculator =
new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.11388, 6000);
+ assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
0.24722, 15000);
@@ -74,7 +73,6 @@ public class BluetoothPowerCalculatorTest {
}
@Test
- @SkipPresubmit("b/180015146")
public void testReportedPowerBasedModel() {
setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
.getOrCreateBluetoothControllerActivityLocked(),
@@ -91,11 +89,10 @@ public class BluetoothPowerCalculatorTest {
BluetoothPowerCalculator calculator =
new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.1, 6000);
+ assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
0.2, 15000);
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 70888905d7c8..496415a43a6a 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -19,18 +19,22 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.power.MeasuredEnergyStats;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -81,6 +85,10 @@ public class CpuPowerCalculatorTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
+ final boolean[] supportedPowerBuckets =
+ new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+ supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+
mStatsRule.getBatteryStats()
.setUserInfoProvider(mMockUserInfoProvider)
.setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
@@ -88,11 +96,11 @@ public class CpuPowerCalculatorTest {
.setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
.setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
.setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
- .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+ .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
+ .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0);
}
@Test
- @SkipPresubmit("b/180015146")
public void testTimerBasedModel() {
when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
@@ -103,28 +111,28 @@ public class CpuPowerCalculatorTest {
// User/System CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
// User/system time in microseconds
callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
return null;
- }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any());
+ }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
// Active CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
callback.onUidCpuTime(APP_UID1, 1111L);
callback.onUidCpuTime(APP_UID2, 3333L);
return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
+ }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
// Per-cluster CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
return null;
- }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
+ }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
@@ -134,7 +142,8 @@ public class CpuPowerCalculatorTest {
CpuPowerCalculator calculator =
new CpuPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
@@ -150,4 +159,64 @@ public class CpuPowerCalculatorTest {
.isWithin(PRECISION).of(2.672322);
assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel() {
+ when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
+
+ when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+ when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
+
+ when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+
+ // User/System CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+ // User/system time in microseconds
+ callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
+ callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
+ return null;
+ }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
+
+ // Active CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
+ callback.onUidCpuTime(APP_UID1, 1111L);
+ callback.onUidCpuTime(APP_UID2, 3333L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+
+ // Per-cluster CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+ callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+ callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+ return null;
+ }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
+
+ final long[] clusterChargesUC = new long[]{13577531, 24688642};
+ mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC);
+
+ mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
+ mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
+
+ CpuPowerCalculator calculator =
+ new CpuPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+ assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+ .isEqualTo(3333);
+ assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(3.18877);
+ assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
+
+ UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+ assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+ .isEqualTo(7777);
+ assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(7.44072);
+ assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
index 0c91b2959f8e..f0111171b83c 100644
--- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -42,9 +42,12 @@ public class CustomMeasuredPowerCalculatorTest {
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
@Test
- @SkipPresubmit("b/180015146")
public void testMeasuredEnergyCopiedIntoBatteryConsumers() {
final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ // For side-effect of creating a BatteryStats.Uid
+ batteryStats.getUidStatsLocked(APP_UID);
+
SparseLongArray uidEnergies = new SparseLongArray();
uidEnergies.put(APP_UID, 30_000_000);
batteryStats.updateCustomMeasuredEnergyStatsLocked(0, 100_000_000, uidEnergies);
@@ -60,18 +63,18 @@ public class CustomMeasuredPowerCalculatorTest {
UidBatteryConsumer uid = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uid.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
- .isWithin(PRECISION).of(2.252252);
+ .isWithin(PRECISION).of(8.333333);
assertThat(uid.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
- .isWithin(PRECISION).of(9.009009);
+ .isWithin(PRECISION).of(33.33333);
SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(
SystemBatteryConsumer.DRAIN_TYPE_CUSTOM);
assertThat(systemConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
- .isWithin(PRECISION).of(7.5075075);
+ .isWithin(PRECISION).of(27.77777);
assertThat(systemConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
- .isWithin(PRECISION).of(15.015015);
+ .isWithin(PRECISION).of(55.55555);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index d50bb05c3588..b89e8bce5fc9 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -232,7 +232,7 @@ public final class LooperStatsTest {
assertThat(entry3.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerSecond");
assertThat(entry3.messageName).startsWith(
- "com.android.internal.os.LooperStatsTest-$$ExternalSyntheticLambda");
+ "com.android.internal.os.LooperStatsTest$$ExternalSyntheticLambda4");
assertThat(entry3.messageCount).isEqualTo(1);
assertThat(entry3.recordedMessageCount).isEqualTo(1);
assertThat(entry3.exceptionCount).isEqualTo(0);
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index a47c4d832083..26adbe9e7c59 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -46,6 +46,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
initTimersAndCounters();
setExternalStatsSyncLocked(new DummyExternalStatsSync());
+ informThatAllExternalStatsAreFlushed();
final boolean[] supportedStandardBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index 6edbbb0ad789..58e2513897ac 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -78,7 +78,6 @@ public class SystemServicePowerCalculatorTest {
}
@Test
- @SkipPresubmit("b/180015146")
public void testPowerProfileBasedModel() {
when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
@@ -154,7 +153,7 @@ public class SystemServicePowerCalculatorTest {
}
@Override
- public void readDelta(@Nullable Callback<long[]> cb) {
+ public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) {
if (cb != null) {
cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index e1005457c289..2e23dc8dbba8 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -17,11 +17,14 @@
package com.android.internal.os;
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
import static com.google.common.truth.Truth.assertThat;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
@@ -50,10 +53,11 @@ public class WifiPowerCalculatorTest {
.setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0)
.setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0)
.setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0)
- .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0);
+ .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0)
+ .initMeasuredEnergyStatsLocked(0);
- @Test
- public void testPowerControllerBasedModel() {
+ /** Sets up a batterystats object with pre-populated network values. */
+ private BatteryStatsImpl setupTestNetworkNumbers() {
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.noteNetworkInterfaceForTransports("wifi",
@@ -64,13 +68,25 @@ public class WifiPowerCalculatorTest {
.insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
mStatsRule.setNetworkStats(networkStats);
- WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000,
+ return batteryStats;
+ }
+
+ /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */
+ private WifiActivityEnergyInfo setupPowerControllerBasedModelEnergyNumbersInfo() {
+ return new WifiActivityEnergyInfo(10000,
WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+ }
+
+ @Test
+ public void testPowerControllerBasedModel_nonMeasured() {
+ final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+ final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.updateWifiState(energyInfo, 1000, 1000);
+ batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
@@ -87,30 +103,54 @@ public class WifiPowerCalculatorTest {
}
@Test
- public void testTimerBasedModel() {
- BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ public void testPowerControllerBasedModel_measured() {
+ final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+ final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.noteNetworkInterfaceForTransports("wifi",
- new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+ batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000);
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
- .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
- mStatsRule.setNetworkStats(networkStats);
+ WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+ mStatsRule.apply(calculator);
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(1423);
+ /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
+
+ SystemBatteryConsumer systemConsumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+ assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(5577);
+ /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+ assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.645200 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
+ }
+
+ /** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */
+ private BatteryStatsImpl setupTimerBasedModelTestNumbers() {
+ final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000);
batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000);
batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000);
batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000);
batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222);
batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444);
+ return batteryStats;
+ }
+
+ @Test
+ public void testTimerBasedModel_nonMeasured() {
+ final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers();
// Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
// on the packet counts.
- batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000);
+ batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
@@ -125,4 +165,31 @@ public class WifiPowerCalculatorTest {
assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.8759216);
}
+
+ @Test
+ public void testTimerBasedModel_measured() {
+ final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers();
+
+ // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
+ // on the packet counts.
+ batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000);
+
+ WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(1000);
+ /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
+
+ SystemBatteryConsumer systemConsumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+ assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(2222);
+ /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+ assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.8759216 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/view/OWNERS b/core/tests/coretests/src/com/android/internal/view/OWNERS
new file mode 100644
index 000000000000..1dad10de5ac7
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/OWNERS
@@ -0,0 +1,3 @@
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTest.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
new file mode 100644
index 000000000000..f8dd1538882b
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
+import android.view.DisplayAdjustments.FixedRotationAdjustments;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link Display}.
+ *
+ * <p>Build/Install/Run:
+ *
+ * atest FrameworksMockingCoreTests:android.view.DisplayTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayTest {
+
+ private static final int APP_WIDTH = 272;
+ private static final int APP_HEIGHT = 700;
+ // Tablet size device, ROTATION_0 corresponds to portrait.
+ private static final int LOGICAL_WIDTH = 700;
+ private static final int LOGICAL_HEIGHT = 1800;
+
+ // Bounds of the app when the device is in portrait mode.
+ private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
+ private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+ // Bounds of the device.
+ private static Rect sDeviceBoundsPortrait = new Rect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT);
+ private static Rect sDeviceBoundsLandscape = new Rect(0, 0, LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+
+ private StaticMockitoSession mMockitoSession;
+
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ private Context mApplicationContext;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ @Before
+ public void setupTests() {
+ mMockitoSession = mockitoSession()
+ .mockStatic(DisplayManagerGlobal.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Ensure no adjustments are set before each test.
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ DisplayAdjustments displayAdjustments =
+ mApplicationContext.getResources().getDisplayAdjustments();
+ displayAdjustments.setFixedRotationAdjustments(null);
+ mApplicationContext.getResources().overrideDisplayAdjustments(null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
+ null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
+ null);
+ mDisplayInfo.rotation = ROTATION_0;
+
+ mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
+ doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
+ }
+
+ @After
+ public void teardownTests() {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ Mockito.framework().clearInlineMocks();
+ }
+
+ @Test
+ public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testConstructor_defaultResources_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ mApplicationContext.getResources().getDisplayAdjustments());
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ // GIVEN display is constructed with display adjustments.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ displayAdjustments);
+ // THEN rotation is not adjusted since no override was set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is not adjusted since no override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is adjusted since an override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_90);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitSandboxed_matchesAppSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesBounds(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait);
+ // GIVEN app bounds do not stretch to include the full DisplayArea.
+ mApplicationContext.getResources().getConfiguration().windowConfiguration
+ .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10));
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches max bounds for the DisplayArea.
+ verifyRealSizeMatchesBounds(display, sDeviceBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesLandscapeSandboxed_matchesAppSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesBounds(display, sAppBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape);
+ // GIVEN app bounds do not stretch to include the full DisplayArea.
+ mApplicationContext.getResources().getConfiguration().windowConfiguration
+ .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10));
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches max bounds for the DisplayArea.
+ verifyRealSizeMatchesBounds(display, sDeviceBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated with an override.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitSandboxed_matchesAppSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesBounds(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait);
+ // GIVEN app bounds do not stretch to include the full DisplayArea.
+ mApplicationContext.getResources().getConfiguration().windowConfiguration
+ .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10));
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches max bounds for the DisplayArea.
+ verifyRealMetricsMatchesBounds(display, sDeviceBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesAppSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesBounds(display, sAppBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+ setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape);
+ // GIVEN app bounds do not stretch to include the full DisplayArea.
+ mApplicationContext.getResources().getConfiguration().windowConfiguration
+ .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10));
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches max bounds for the DisplayArea.
+ verifyRealMetricsMatchesBounds(display, sDeviceBoundsLandscape);
+ }
+
+ // Given rotated display dimensions, calculate the letterboxed app bounds.
+ private static Rect buildAppBounds(int displayWidth, int displayHeight) {
+ final int midWidth = displayWidth / 2;
+ final int left = midWidth - (APP_WIDTH / 2);
+ final int right = midWidth + (APP_WIDTH / 2);
+ final int midHeight = displayHeight / 2;
+ // Coordinate system starts at top left.
+ final int top = midHeight - (APP_HEIGHT / 2);
+ final int bottom = midHeight + (APP_HEIGHT / 2);
+ return new Rect(left, top, right, bottom);
+ }
+
+ private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_90;
+ // Flip width & height assignment since the device is rotated.
+ displayInfo.logicalWidth = LOGICAL_HEIGHT;
+ displayInfo.logicalHeight = LOGICAL_WIDTH;
+ }
+
+ private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_0;
+ displayInfo.logicalWidth = LOGICAL_WIDTH;
+ displayInfo.logicalHeight = LOGICAL_HEIGHT;
+ }
+
+ /**
+ * Set max bounds to be sandboxed to the app bounds, indicating the app is in
+ * size compat mode or letterbox.
+ */
+ private static void setMaxBoundsSandboxed(Resources resources, Rect bounds) {
+ resources.getConfiguration().windowConfiguration.setMaxBounds(bounds);
+ }
+
+ /**
+ * Do not compare entire display info, since it is updated to match display the test is run on.
+ */
+ private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+ assertThat(actual.displayId).isEqualTo(expected.displayId);
+ assertThat(actual.rotation).isEqualTo(expected.rotation);
+ assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
+ assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeIsLandscape(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ // Flip the width and height check since the device is rotated.
+ assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
+ }
+
+ private static void verifyRealMetricsIsLandscape(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ // Flip the width and height check since the device is rotated.
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
+ }
+
+ private static void verifyRealSizeIsPortrait(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
+ }
+
+ private static void verifyRealMetricsIsPortrait(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeMatchesBounds(Display display, Rect bounds) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(bounds.width(), bounds.height()));
+ }
+
+ private static void verifyRealMetricsMatchesBounds(Display display, Rect bounds) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(bounds.width());
+ assertThat(metrics.heightPixels).isEqualTo(bounds.height());
+ }
+
+ private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
+ Resources resources, @Surface.Rotation int rotation) {
+ FixedRotationAdjustments fixedRotationAdjustments =
+ setFixedRotationAdjustments(resources, rotation);
+ resources.overrideDisplayAdjustments(
+ buildOverrideRotationAdjustments(fixedRotationAdjustments));
+ return fixedRotationAdjustments;
+ }
+
+ private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+ @Surface.Rotation int rotation) {
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
+ return fixedRotationAdjustments;
+ }
+
+ private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
+ FixedRotationAdjustments fixedRotationAdjustments) {
+ return consumedDisplayAdjustments
+ -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
deleted file mode 100644
index 7175f562d7ef..000000000000
--- a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.util;
-
-import static android.Manifest.permission.NETWORK_SETTINGS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.HashMap;
-
-/** Unit tests for {@link LocationPermissionChecker}. */
-public class LocationPermissionCheckerTest {
-
- public static final String TAG = "ConnectivityUtilTest";
-
- // Mock objects for testing
- @Mock private Context mMockContext;
- @Mock private PackageManager mMockPkgMgr;
- @Mock private ApplicationInfo mMockApplInfo;
- @Mock private AppOpsManager mMockAppOps;
- @Mock private UserManager mMockUserManager;
- @Mock private LocationManager mLocationManager;
-
- private static final String TEST_PKG_NAME = "com.google.somePackage";
- private static final String TEST_FEATURE_ID = "com.google.someFeature";
- private static final int MANAGED_PROFILE_UID = 1100000;
- private static final int OTHER_USER_UID = 1200000;
-
- private final String mInteractAcrossUsersFullPermission =
- "android.permission.INTERACT_ACROSS_USERS_FULL";
- private final String mManifestStringCoarse =
- Manifest.permission.ACCESS_COARSE_LOCATION;
- private final String mManifestStringFine =
- Manifest.permission.ACCESS_FINE_LOCATION;
-
- // Test variables
- private int mWifiScanAllowApps;
- private int mUid;
- private int mCoarseLocationPermission;
- private int mAllowCoarseLocationApps;
- private int mFineLocationPermission;
- private int mAllowFineLocationApps;
- private int mNetworkSettingsPermission;
- private int mCurrentUser;
- private boolean mIsLocationEnabled;
- private boolean mThrowSecurityException;
- private Answer<Integer> mReturnPermission;
- private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
- private LocationPermissionChecker mChecker;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- initTestVars();
- }
-
- private void setupMocks() throws Exception {
- when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
- .thenReturn(mMockApplInfo);
- when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
- when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
- TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
- eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
- .thenReturn(mAllowCoarseLocationApps);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
- eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
- .thenReturn(mAllowFineLocationApps);
- if (mThrowSecurityException) {
- doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
- + " to application bound to user " + mUid))
- .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
- }
- when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
- .thenReturn(mMockAppOps);
- when(mMockContext.getSystemService(Context.USER_SERVICE))
- .thenReturn(mMockUserManager);
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
- }
-
- private void setupTestCase() throws Exception {
- setupMocks();
- setupMockInterface();
- mChecker = new LocationPermissionChecker(mMockContext);
- }
-
- private void initTestVars() {
- mPermissionsList.clear();
- mReturnPermission = createPermissionAnswer();
- mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
- mUid = OTHER_USER_UID;
- mThrowSecurityException = true;
- mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
- mIsLocationEnabled = false;
- mCurrentUser = ActivityManager.getCurrentUser();
- mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
- mFineLocationPermission = PackageManager.PERMISSION_DENIED;
- mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
- mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
- mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED;
- }
-
- private void setupMockInterface() {
- Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
- doAnswer(mReturnPermission).when(mMockContext).checkPermission(
- anyString(), anyInt(), anyInt());
- when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
- UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
- .thenReturn(true);
- when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
- .thenReturn(mCoarseLocationPermission);
- when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
- .thenReturn(mFineLocationPermission);
- when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid))
- .thenReturn(mNetworkSettingsPermission);
- when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
- }
-
- private Answer<Integer> createPermissionAnswer() {
- return new Answer<Integer>() {
- @Override
- public Integer answer(InvocationOnMock invocation) {
- int myUid = (int) invocation.getArguments()[1];
- String myPermission = (String) invocation.getArguments()[0];
- mPermissionsList.get(myPermission);
- if (mPermissionsList.containsKey(myPermission)) {
- int uid = mPermissionsList.get(myPermission);
- if (myUid == uid) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- return PackageManager.PERMISSION_DENIED;
- }
- };
- }
-
- @Test
- public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
- mIsLocationEnabled = true;
- mThrowSecurityException = false;
- mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- mUid = mCurrentUser;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.SUCCEEDED, result);
- }
-
- @Test
- public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
- mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- mIsLocationEnabled = true;
- mThrowSecurityException = false;
- mUid = mCurrentUser;
- mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.SUCCEEDED, result);
- }
-
- @Test
- public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
- mThrowSecurityException = true;
- mIsLocationEnabled = true;
- mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- setupTestCase();
-
- assertThrows(SecurityException.class,
- () -> mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
- }
-
- @Test
- public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
- mThrowSecurityException = false;
- mIsLocationEnabled = true;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
- }
-
- @Test
- public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
- mThrowSecurityException = false;
- mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- mIsLocationEnabled = true;
- mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
- mUid = MANAGED_PROFILE_UID;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
- verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
- }
-
- @Test
- public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
- mThrowSecurityException = false;
- mUid = MANAGED_PROFILE_UID;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
- mIsLocationEnabled = false;
-
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result);
- }
-
- @Test
- public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings()
- throws Exception {
- mThrowSecurityException = false;
- mIsLocationEnabled = false;
- mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.SUCCEEDED, result);
- }
-
-
- private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
- try {
- r.run();
- Assert.fail("Expected " + exceptionClass + " to be thrown.");
- } catch (Exception exception) {
- assertTrue(exceptionClass.isInstance(exception));
- }
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index c01bb75c32aa..e41805dd3a59 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -22,7 +22,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -32,6 +31,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.concurrent.Executor;
@@ -42,51 +42,23 @@ import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class RangingManagerTest {
- private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
private static final PersistableBundle PARAMS = new PersistableBundle();
private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
@Test
public void testOpenSession_OpenRangingInvoked() throws RemoteException {
- RangingManager rangingManager = new RangingManager(ADAPTER);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
rangingManager.openSession(PARAMS, EXECUTOR, callback);
- verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS));
- }
-
- @Test
- public void testOpenSession_ErrorIfSameSessionHandleReturned() throws RemoteException {
- RangingManager rangingManager = new RangingManager(ADAPTER);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- SessionHandle handle = new SessionHandle(1);
- when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
- rangingManager.openSession(PARAMS, EXECUTOR, callback);
-
- // Calling openSession will cause the same session handle to be returned. The onClosed
- // callback should be invoked
- RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
- rangingManager.openSession(PARAMS, EXECUTOR, callback2);
- verify(callback, times(0)).onClosed(anyInt(), any());
- verify(callback2, times(1)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testOnRangingOpened_ValidSessionHandle() throws RemoteException {
- RangingManager rangingManager = new RangingManager(ADAPTER);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- SessionHandle handle = new SessionHandle(1);
- when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
- rangingManager.openSession(PARAMS, EXECUTOR, callback);
- rangingManager.onRangingOpened(handle);
- verify(callback, times(1)).onOpened(any());
+ verify(adapter, times(1)).openRanging(any(), eq(rangingManager), eq(PARAMS));
}
@Test
public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
- RangingManager rangingManager = new RangingManager(ADAPTER);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
rangingManager.onRangingOpened(new SessionHandle(2));
@@ -95,18 +67,20 @@ public class RangingManagerTest {
@Test
public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
- SessionHandle sessionHandle1 = new SessionHandle(1);
- SessionHandle sessionHandle2 = new SessionHandle(2);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+ ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+ ArgumentCaptor.forClass(SessionHandle.class);
- when(ADAPTER.openRanging(any(), any()))
- .thenReturn(sessionHandle1)
- .thenReturn(sessionHandle2);
-
- RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingManager rangingManager = new RangingManager(adapter);
rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+ verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+ verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
rangingManager.onRangingOpened(sessionHandle1);
verify(callback1, times(1)).onOpened(any());
@@ -119,12 +93,17 @@ public class RangingManagerTest {
@Test
public void testCorrectCallbackInvoked() throws RemoteException {
- RangingManager rangingManager = new RangingManager(ADAPTER);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- SessionHandle handle = new SessionHandle(1);
- when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+ ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+ ArgumentCaptor.forClass(SessionHandle.class);
rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle handle = sessionHandleCaptor.getValue();
+
rangingManager.onRangingOpened(handle);
verify(callback, times(1)).onOpened(any());
@@ -156,20 +135,23 @@ public class RangingManagerTest {
@Test
public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
// Verify that if multiple sessions are registered, only the session that is
// requested to close receives the associated callbacks
- SessionHandle sessionHandle1 = new SessionHandle(1);
- SessionHandle sessionHandle2 = new SessionHandle(2);
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
- when(ADAPTER.openRanging(any(), any()))
- .thenReturn(sessionHandle1)
- .thenReturn(sessionHandle2);
+ RangingManager rangingManager = new RangingManager(adapter);
+ ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+ ArgumentCaptor.forClass(SessionHandle.class);
- RangingManager rangingManager = new RangingManager(ADAPTER);
rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+ verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+ verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
verify(callback1, times(1)).onClosed(anyInt(), any());
@@ -182,19 +164,22 @@ public class RangingManagerTest {
@Test
public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
- SessionHandle sessionHandle1 = new SessionHandle(1);
- SessionHandle sessionHandle2 = new SessionHandle(2);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
- when(ADAPTER.openRanging(any(), any()))
- .thenReturn(sessionHandle1)
- .thenReturn(sessionHandle2);
+ ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+ ArgumentCaptor.forClass(SessionHandle.class);
- RangingManager rangingManager = new RangingManager(ADAPTER);
+ RangingManager rangingManager = new RangingManager(adapter);
rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+ verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
rangingManager.onRangingStarted(sessionHandle1, PARAMS);
rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+ verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
rangingManager.onRangingStarted(sessionHandle2, PARAMS);
rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
@@ -232,17 +217,24 @@ public class RangingManagerTest {
private void runReason(@RangingChangeReason int reasonIn,
@RangingSession.Callback.Reason int reasonOut) throws RemoteException {
- RangingManager rangingManager = new RangingManager(ADAPTER);
+ IUwbAdapter adapter = mock(IUwbAdapter.class);
+ RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
- SessionHandle handle = new SessionHandle(1);
- when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+ ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+ ArgumentCaptor.forClass(SessionHandle.class);
+
rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ SessionHandle handle = sessionHandleCaptor.getValue();
rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
// Open a new session
rangingManager.openSession(PARAMS, EXECUTOR, callback);
+ verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+ handle = sessionHandleCaptor.getValue();
rangingManager.onRangingOpened(handle);
rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index 8e7f7c562ade..75c6924a1939 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -16,34 +16,23 @@
package android.uwb;
-import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.SystemClock;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.Executor;
public class UwbTestUtils {
private UwbTestUtils() {}
- public static boolean isUwbSupported(Context context) {
- PackageManager packageManager = context.getPackageManager();
- return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB);
- }
-
public static AngleMeasurement getAngleMeasurement() {
- return new AngleMeasurement.Builder()
- .setRadians(getDoubleInRange(-Math.PI, Math.PI))
- .setErrorRadians(getDoubleInRange(0, Math.PI))
- .setConfidenceLevel(getDoubleInRange(0, 1))
- .build();
+ return new AngleMeasurement(
+ getDoubleInRange(-Math.PI, Math.PI),
+ getDoubleInRange(0, Math.PI),
+ getDoubleInRange(0, 1));
}
public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
- return new AngleOfArrivalMeasurement.Builder()
+ return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
.setAltitude(getAngleMeasurement())
- .setAzimuth(getAngleMeasurement())
.build();
}
@@ -69,14 +58,6 @@ public class UwbTestUtils {
.build();
}
- public static List<RangingMeasurement> getRangingMeasurements(int num) {
- List<RangingMeasurement> result = new ArrayList<>();
- for (int i = 0; i < num; i++) {
- result.add(getRangingMeasurement());
- }
- return result;
- }
-
public static RangingReport getRangingReports(int numMeasurements) {
RangingReport.Builder builder = new RangingReport.Builder();
for (int i = 0; i < numMeasurements; i++) {
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 89644e2320c1..8991a6117306 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-
-
// Privapp permission whitelist files
package {
@@ -26,6 +24,14 @@ package {
}
prebuilt_etc {
+ name: "allowed_privapp_com.android.carsystemui",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.carsystemui.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "allowed_privapp_android.car.cluster.loggingrenderer",
sub_dir: "permissions",
src: "android.car.cluster.loggingrenderer.xml",
@@ -187,3 +193,17 @@ prebuilt_etc {
src: "com.android.car.activityresolver.xml",
filename_from_src: true,
}
+
+prebuilt_etc {
+ name: "allowed_privapp_com.android.car.cluster.home",
+ sub_dir: "permissions",
+ src: "com.android.car.cluster.home.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "allowed_privapp_com.android.car.messenger",
+ sub_dir: "permissions",
+ src: "com.android.car.messenger.xml",
+ filename_from_src: true,
+}
diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml
new file mode 100644
index 000000000000..4c2d614df5b0
--- /dev/null
+++ b/data/etc/car/com.android.car.cluster.home.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.cluster.home">
+ <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.messenger.xml b/data/etc/car/com.android.car.messenger.xml
new file mode 100644
index 000000000000..16595c30c65c
--- /dev/null
+++ b/data/etc/car/com.android.car.messenger.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.messenger">
+ <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml
index 6132d53b4651..58306be90048 100644
--- a/data/etc/car/com.android.car.shell.xml
+++ b/data/etc/car/com.android.car.shell.xml
@@ -20,5 +20,11 @@
<privapp-permissions package="com.android.shell">
<permission name="android.permission.INSTALL_PACKAGES" />
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
+ <permission name="android.car.permission.CAR_DIAGNOSTICS"/>
+ <permission name="android.car.permission.CAR_DRIVING_STATE"/>
+ <permission name="android.car.permission.CAR_POWER"/>
+ <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.carsystemui.xml b/data/etc/car/com.android.carsystemui.xml
new file mode 100644
index 000000000000..4e2f29438531
--- /dev/null
+++ b/data/etc/car/com.android.carsystemui.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.systemui">
+ <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
+ <permission name="android.car.permission.CAR_ENROLL_TRUST"/>
+ <permission name="android.car.permission.CAR_POWER"/>
+ <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 77a38a9bb1a0..b1715991a099 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -154,6 +154,8 @@
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" />
<assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" />
+ <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" />
+ <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="media" />
<assign-permission name="android.permission.INTERNET" uid="media" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c49fe8563dab..31cdaeb84077 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -488,6 +488,10 @@ applications that come with the platform
<permission name="android.permission.MANAGE_UI_TRANSLATION" />
<!-- Permission required for CTS test - ClipboardManagerTest -->
<permission name="android.permission.SET_CLIP_SOURCE" />
+ <!-- Permission required for CTS test - FontManagerTest -->
+ <permission name="android.permission.UPDATE_FONTS" />
+ <!-- Permission required for hotword detection service CTS tests -->
+ <permission name="android.permission.MANAGE_HOTWORD_DETECTION" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b7bf8ab75799..5b57f1901550 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -307,6 +307,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1777196134": {
+ "message": "goodToGo(): No apps to animate, mPendingAnimations=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"-1770075711": {
"message": "Adding window client %s that is dead, aborting.",
"level": "WARN",
@@ -817,12 +823,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
- "-1159577965": {
- "message": "Focus requested for input consumer=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_FOCUS_LIGHT",
- "at": "com\/android\/server\/wm\/InputMonitor.java"
- },
"-1156118957": {
"message": "Updated config=%s",
"level": "DEBUG",
@@ -1219,6 +1219,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DragState.java"
},
+ "-681380736": {
+ "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing from mismatch with parent bounds? %s size compat mode %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-677449371": {
"message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b",
"level": "DEBUG",
@@ -1981,12 +1987,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "194124419": {
- "message": "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
- },
"200829729": {
"message": "ScreenRotationAnimation onAnimationEnd",
"level": "DEBUG",
@@ -2083,6 +2083,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DragState.java"
},
+ "269976641": {
+ "message": "goodToGo(): Animation canceled already",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"274773837": {
"message": "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL transit=%s Callers=%s",
"level": "VERBOSE",
@@ -2467,12 +2473,6 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "690411811": {
- "message": "goodToGo(): No apps to animate",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
- },
"691515534": {
"message": " Commit wallpaper becoming invisible: %s",
"level": "VERBOSE",
@@ -2593,12 +2593,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "883475718": {
- "message": "Report configuration: %s %s %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityClientController.java"
- },
"892244061": {
"message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
"level": "INFO",
@@ -2905,6 +2899,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "1305412562": {
+ "message": "Report configuration: %s %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/ActivityClientController.java"
+ },
"1316533291": {
"message": "State movement: %s from:%s to:%s reason:%s",
"level": "VERBOSE",
@@ -3307,6 +3307,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
+ "1804245629": {
+ "message": "Attempted to add starting window to token but already cleaned",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"1810019902": {
"message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps",
"level": "DEBUG",
@@ -3433,6 +3439,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "1931178855": {
+ "message": "\tnonApp=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"1947239194": {
"message": "Deferring rotation, still finishing previous rotation",
"level": "VERBOSE",
@@ -3481,6 +3493,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1999594750": {
+ "message": "startAnimation",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
+ },
"2018454757": {
"message": "WS.removeImmediately: %s Already removed...",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0957_Product_0001.idc b/data/keyboards/Vendor_0957_Product_0001.idc
index e1f4346369f3..39479ce725e1 100644
--- a/data/keyboards/Vendor_0957_Product_0001.idc
+++ b/data/keyboards/Vendor_0957_Product_0001.idc
@@ -19,5 +19,4 @@
# Basic Parameters
keyboard.layout = Vendor_0957_Product_0001
-keyboard.characterMap = Vendor_0957_Product_0001
audio.mic = 1 \ No newline at end of file
diff --git a/data/keyboards/Vendor_248a_Product_8266.idc b/data/keyboards/Vendor_248a_Product_8266.idc
new file mode 100644
index 000000000000..3021655c286e
--- /dev/null
+++ b/data/keyboards/Vendor_248a_Product_8266.idc
@@ -0,0 +1,24 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Input Device Configuration file for Google Reference RCU Remote.
+#
+#
+
+# Basic Parameters
+# Due to a memory error on early prototypes of the reference remote control
+# the VID/PID is mapped to 248a/8266 instead of 0957/0001
+keyboard.layout = Vendor_0957_Product_0001
+audio.mic = 1 \ No newline at end of file
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c80788269c24..2a6bbf36ef76 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,6 +25,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.annotation.UiThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
@@ -169,6 +170,21 @@ public class Typeface {
Collections.emptyMap();
/**
+ * Returns the shared memory that used for creating Typefaces.
+ *
+ * @return A SharedMemory used for creating Typeface. Maybe null if the lazy initialization is
+ * disabled or inside SystemServer or Zygote.
+ * @hide
+ */
+ @TestApi
+ public static @Nullable SharedMemory getSystemFontMapSharedMemory() {
+ if (ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+ Objects.requireNonNull(sSystemFontMapSharedMemory);
+ }
+ return sSystemFontMapSharedMemory;
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -1196,8 +1212,13 @@ public class Typeface {
}
}
- /** @hide */
- public static SharedMemory serializeFontMap(Map<String, Typeface> fontMap)
+ /**
+ * Create a serialized system font mappings.
+ *
+ * @hide
+ */
+ @TestApi
+ public static @NonNull SharedMemory serializeFontMap(@NonNull Map<String, Typeface> fontMap)
throws IOException, ErrnoException {
long[] nativePtrs = new long[fontMap.size()];
// The name table will not be large, so let's create a byte array in memory.
@@ -1229,9 +1250,14 @@ public class Typeface {
}
// buffer's byte order should be BIG_ENDIAN.
- /** @hide */
- @VisibleForTesting
- public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
+ /**
+ * Deserialize the font mapping from the serialized byte buffer.
+ *
+ * @hide
+ */
+ @TestApi
+ public static @NonNull Map<String, Typeface> deserializeFontMap(@NonNull ByteBuffer buffer)
+ throws IOException {
Map<String, Typeface> fontMap = new ArrayMap<>();
int typefacesBytesCount = buffer.getInt();
long[] nativePtrs = nativeReadTypefaces(buffer.slice());
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 80f65f919fa6..b57f7af14a0e 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -27,8 +27,9 @@ import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.animation.RenderNodeAnimator;
import android.util.ArraySet;
-import android.view.animation.DecelerateInterpolator;
+import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
import java.util.function.Consumer;
@@ -36,32 +37,35 @@ import java.util.function.Consumer;
* @hide
*/
public final class RippleAnimationSession {
- private static final int ENTER_ANIM_DURATION = 350;
- private static final int EXIT_ANIM_OFFSET = ENTER_ANIM_DURATION;
- private static final int EXIT_ANIM_DURATION = 350;
+ private static final String TAG = "RippleAnimationSession";
+ private static final int ENTER_ANIM_DURATION = 450;
+ private static final int EXIT_ANIM_DURATION = 300;
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- // Matches R.interpolator.fast_out_slow_in but as we have no context we can't just import that
- private static final TimeInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
-
+ private static final TimeInterpolator PATH_INTERPOLATOR =
+ new PathInterpolator(.2f, 0, 0, 1f);
private Consumer<RippleAnimationSession> mOnSessionEnd;
- private AnimationProperties<Float, Paint> mProperties;
+ private final AnimationProperties<Float, Paint> mProperties;
private AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>> mCanvasProperties;
private Runnable mOnUpdate;
private long mStartTime;
private boolean mForceSoftware;
- private ArraySet<Animator> mActiveAnimations = new ArraySet(3);
+ private final ValueAnimator mSparkle = ValueAnimator.ofFloat(0, 1);
RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
boolean forceSoftware) {
mProperties = properties;
mForceSoftware = forceSoftware;
- }
- void end() {
- for (Animator anim: mActiveAnimations) {
- if (anim != null) anim.end();
- }
- mActiveAnimations.clear();
+ mSparkle.addUpdateListener(anim -> {
+ final long now = AnimationUtils.currentAnimationTimeMillis();
+ final long elapsed = now - mStartTime - ENTER_ANIM_DURATION;
+ final float phase = (float) elapsed / 30000f;
+ mProperties.getShader().setNoisePhase(phase);
+ notifyUpdate();
+ });
+ mSparkle.setDuration(ENTER_ANIM_DURATION);
+ mSparkle.setInterpolator(LINEAR_INTERPOLATOR);
+ mSparkle.setRepeatCount(ValueAnimator.INFINITE);
}
@NonNull RippleAnimationSession enter(Canvas canvas) {
@@ -70,7 +74,7 @@ public final class RippleAnimationSession {
} else {
enterSoftware();
}
- mStartTime = System.nanoTime();
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
return this;
}
@@ -81,7 +85,7 @@ public final class RippleAnimationSession {
}
private void onAnimationEnd(Animator anim) {
- mActiveAnimations.remove(anim);
+ notifyUpdate();
}
@NonNull RippleAnimationSession setOnSessionEnd(
@@ -92,7 +96,6 @@ public final class RippleAnimationSession {
RippleAnimationSession setOnAnimationUpdated(@Nullable Runnable run) {
mOnUpdate = run;
- mProperties.setOnChange(mOnUpdate);
return this;
}
@@ -112,24 +115,22 @@ public final class RippleAnimationSession {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mSparkle.end();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
}
});
expand.setInterpolator(LINEAR_INTERPOLATOR);
expand.start();
- mActiveAnimations.add(expand);
}
private long computeDelay() {
- long currentTime = System.nanoTime();
- long timePassed = (currentTime - mStartTime) / 1_000_000;
- long difference = EXIT_ANIM_OFFSET;
- return Math.max(difference - timePassed, 0);
+ final long timePassed = AnimationUtils.currentAnimationTimeMillis() - mStartTime;
+ return Math.max((long) ENTER_ANIM_DURATION - timePassed, 0);
}
+
private void notifyUpdate() {
- Runnable onUpdate = mOnUpdate;
- if (onUpdate != null) onUpdate.run();
+ if (mOnUpdate != null) mOnUpdate.run();
}
RippleAnimationSession setForceSoftwareAnimation(boolean forceSw) {
@@ -148,30 +149,36 @@ public final class RippleAnimationSession {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mSparkle.end();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
}
});
exit.setTarget(canvas);
- exit.setInterpolator(DECELERATE_INTERPOLATOR);
+ exit.setInterpolator(LINEAR_INTERPOLATOR);
long delay = computeDelay();
exit.setStartDelay(delay);
exit.start();
- mActiveAnimations.add(exit);
}
- private void enterHardware(RecordingCanvas can) {
+ private void enterHardware(RecordingCanvas canvas) {
AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>>
props = getCanvasProperties();
RenderNodeAnimator expand =
new RenderNodeAnimator(props.getProgress(), .5f);
- expand.setTarget(can);
+ expand.setTarget(canvas);
+ startAnimation(expand);
+ }
+
+ private void startAnimation(Animator expand) {
expand.setDuration(ENTER_ANIM_DURATION);
expand.addListener(new AnimatorListener(this));
expand.setInterpolator(LINEAR_INTERPOLATOR);
expand.start();
- mActiveAnimations.add(expand);
+ if (!mSparkle.isRunning()) {
+ mSparkle.start();
+ }
}
private void enterSoftware() {
@@ -180,17 +187,15 @@ public final class RippleAnimationSession {
notifyUpdate();
mProperties.getShader().setProgress((Float) expand.getAnimatedValue());
});
- expand.addListener(new AnimatorListener(this));
- expand.setInterpolator(LINEAR_INTERPOLATOR);
- expand.start();
- mActiveAnimations.add(expand);
+ startAnimation(expand);
}
@NonNull AnimationProperties<Float, Paint> getProperties() {
return mProperties;
}
- @NonNull AnimationProperties getCanvasProperties() {
+ @NonNull
+ AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>> getCanvasProperties() {
if (mCanvasProperties == null) {
mCanvasProperties = new AnimationProperties<>(
CanvasProperty.createFloat(mProperties.getX()),
@@ -209,6 +214,7 @@ public final class RippleAnimationSession {
AnimatorListener(RippleAnimationSession session) {
mSession = session;
}
+
@Override
public void onAnimationStart(Animator animation) {
@@ -231,21 +237,12 @@ public final class RippleAnimationSession {
}
static class AnimationProperties<FloatType, PaintType> {
- private final FloatType mY;
- private FloatType mProgress;
- private FloatType mMaxRadius;
+ private final FloatType mProgress;
+ private final FloatType mMaxRadius;
private final PaintType mPaint;
- private final FloatType mX;
private final RippleShader mShader;
- private Runnable mOnChange;
-
- private void onChange() {
- if (mOnChange != null) mOnChange.run();
- }
-
- private void setOnChange(Runnable onChange) {
- mOnChange = onChange;
- }
+ private FloatType mX;
+ private FloatType mY;
AnimationProperties(FloatType x, FloatType y, FloatType maxRadius,
PaintType paint, FloatType progress, RippleShader shader) {
@@ -261,6 +258,11 @@ public final class RippleAnimationSession {
return mProgress;
}
+ void setOrigin(FloatType x, FloatType y) {
+ mX = x;
+ mY = y;
+ }
+
FloatType getX() {
return mX;
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 5024875aab3c..fb2b9b343f0a 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -48,6 +48,7 @@ import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.Shader;
import android.os.Build;
+import android.os.SystemProperties;
import android.util.AttributeSet;
import android.view.animation.LinearInterpolator;
@@ -109,17 +110,6 @@ import java.util.Arrays;
* </pre>
*
* @attr ref android.R.styleable#RippleDrawable_color
- *
- * To change the ripple style, assign the value of "solid" or "patterned" to the android:rippleStyle
- * attribute.
- *
- * <pre>
- * <code>&lt;!-- A red ripple masked against an opaque rectangle. --/>
- * &lt;ripple android:rippleStyle="patterned">
- * &lt;/ripple></code>
- * </pre>
- *
- * @attr ref android.R.styleable#RippleDrawable_rippleStyle
*/
public class RippleDrawable extends LayerDrawable {
/**
@@ -131,12 +121,14 @@ public class RippleDrawable extends LayerDrawable {
/**
* Ripple style where a solid circle is drawn. This is also the default style
* @see #setRippleStyle(int)
+ * @hide
*/
public static final int STYLE_SOLID = 0;
/**
* Ripple style where a circle shape with a patterned,
* noisy interior expands from the hotspot to the bounds".
* @see #setRippleStyle(int)
+ * @hide
*/
public static final int STYLE_PATTERNED = 1;
@@ -159,6 +151,9 @@ public class RippleDrawable extends LayerDrawable {
/** The maximum number of ripples supported. */
private static final int MAX_RIPPLES = 10;
private static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+ /** Temporary flag for teamfood. **/
+ private static final boolean FORCE_PATTERNED_STYLE =
+ SystemProperties.getBoolean("persist.material.patternedripple", false);
private final Rect mTempRect = new Rect();
@@ -361,7 +356,9 @@ public class RippleDrawable extends LayerDrawable {
}
} else {
if (focused || hovered) {
- enterPatternedBackgroundAnimation(focused, hovered);
+ if (!pressed) {
+ enterPatternedBackgroundAnimation(focused, hovered);
+ }
} else {
exitPatternedBackgroundAnimation();
}
@@ -571,7 +568,10 @@ public class RippleDrawable extends LayerDrawable {
mState.mMaxRadius = a.getDimensionPixelSize(
R.styleable.RippleDrawable_radius, mState.mMaxRadius);
- mState.mRippleStyle = a.getInteger(R.styleable.RippleDrawable_rippleStyle, STYLE_SOLID);
+ if (!FORCE_PATTERNED_STYLE) {
+ mState.mRippleStyle = a.getInteger(R.styleable.RippleDrawable_rippleStyle,
+ mState.mRippleStyle);
+ }
}
private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
@@ -812,21 +812,25 @@ public class RippleDrawable extends LayerDrawable {
}
private void drawPatterned(@NonNull Canvas canvas) {
- final Rect bounds = getBounds();
+ final Rect bounds = getDirtyBounds();
final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
boolean useCanvasProps = shouldUseCanvasProps(canvas);
boolean changedHotspotBounds = !bounds.equals(mHotspotBounds);
if (isBounded()) {
- canvas.clipRect(mHotspotBounds);
+ canvas.clipRect(bounds);
}
- float x, y;
+ float x, y, w, h;
if (changedHotspotBounds) {
x = mHotspotBounds.exactCenterX();
y = mHotspotBounds.exactCenterY();
+ w = mHotspotBounds.width();
+ h = mHotspotBounds.height();
useCanvasProps = false;
} else {
x = mPendingX;
y = mPendingY;
+ w = bounds.width();
+ h = bounds.height();
}
boolean shouldAnimate = mRippleActive;
boolean shouldExit = mExitingAnimation;
@@ -837,9 +841,9 @@ public class RippleDrawable extends LayerDrawable {
drawPatternedBackground(canvas);
if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
RippleAnimationSession.AnimationProperties<Float, Paint> properties =
- createAnimationProperties(x, y);
+ createAnimationProperties(x, y, w, h);
mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
- .setOnAnimationUpdated(useCanvasProps ? null : () -> invalidateSelf(false))
+ .setOnAnimationUpdated(() -> invalidateSelf(false))
.setOnSessionEnd(session -> {
mRunningAnimations.remove(session);
})
@@ -864,20 +868,8 @@ public class RippleDrawable extends LayerDrawable {
} else {
RippleAnimationSession.AnimationProperties<Float, Paint> p =
s.getProperties();
- float posX, posY;
- if (changedHotspotBounds) {
- posX = x;
- posY = y;
- if (p.getPaint().getShader() instanceof RippleShader) {
- RippleShader shader = (RippleShader) p.getPaint().getShader();
- shader.setOrigin(posX, posY);
- }
- } else {
- posX = p.getX();
- posY = p.getY();
- }
float radius = p.getMaxRadius();
- canvas.drawCircle(posX, posY, radius, p.getPaint());
+ canvas.drawCircle(p.getX(), p.getY(), radius, p.getPaint());
}
}
canvas.restoreToCount(saveCount);
@@ -905,14 +897,13 @@ public class RippleDrawable extends LayerDrawable {
private float computeRadius() {
Rect b = getDirtyBounds();
- float gap = 0;
- float radius = (float) Math.sqrt(b.width() * b.width() + b.height() * b.height()) / 2 + gap;
+ float radius = (float) Math.sqrt(b.width() * b.width() + b.height() * b.height()) / 2;
return radius;
}
@NonNull
private RippleAnimationSession.AnimationProperties<Float, Paint> createAnimationProperties(
- float x, float y) {
+ float x, float y, float w, float h) {
Paint p = new Paint(mRipplePaint);
float radius = mState.mMaxRadius;
RippleAnimationSession.AnimationProperties<Float, Paint> properties;
@@ -920,19 +911,19 @@ public class RippleDrawable extends LayerDrawable {
int color = mMaskColorFilter == null
? mState.mColor.getColorForState(getState(), Color.BLACK)
: mMaskColorFilter.getColor();
- color = color | 0xFF000000;
shader.setColor(color);
- shader.setOrigin(x, y);
+ shader.setOrigin(w / 2, y / 2);
+ shader.setTouch(x, y);
+ shader.setResolution(w, h);
+ shader.setNoisePhase(0);
shader.setRadius(radius);
shader.setProgress(.0f);
properties = new RippleAnimationSession.AnimationProperties<>(
- x, y, radius, p, 0f,
- shader);
+ w / 2, h / 2, radius, p, 0f, shader);
if (mMaskShader == null) {
- shader.setHasMask(false);
+ shader.setShader(null);
} else {
shader.setShader(mMaskShader);
- shader.setHasMask(true);
}
p.setShader(shader);
p.setColorFilter(null);
@@ -1160,7 +1151,7 @@ public class RippleDrawable extends LayerDrawable {
// The ripple timing depends on the paint's alpha value, so we need
// to push just the alpha channel into the paint and let the filter
// handle the full-alpha color.
- int maskColor = color | 0xFF000000;
+ int maskColor = mState.mRippleStyle == STYLE_PATTERNED ? color : color | 0xFF000000;
if (mMaskColorFilter.getColor() != maskColor) {
mMaskColorFilter = new PorterDuffColorFilter(maskColor, mMaskColorFilter.getMode());
}
@@ -1248,6 +1239,7 @@ public class RippleDrawable extends LayerDrawable {
* @see #STYLE_PATTERNED
*
* @param style The style of the ripple
+ * @hide
*/
public void setRippleStyle(@RippleStyle int style) throws IllegalArgumentException {
if (style == STYLE_SOLID || style == STYLE_PATTERNED) {
@@ -1260,6 +1252,7 @@ public class RippleDrawable extends LayerDrawable {
/**
* Get the current ripple style
* @return Ripple style
+ * @hide
*/
public @RippleStyle int getRippleStyle() {
return mState.mRippleStyle;
@@ -1276,7 +1269,7 @@ public class RippleDrawable extends LayerDrawable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
int mMaxRadius = RADIUS_AUTO;
- int mRippleStyle = STYLE_SOLID;
+ int mRippleStyle = FORCE_PATTERNED_STYLE ? STYLE_PATTERNED : STYLE_SOLID;
public RippleState(LayerState orig, RippleDrawable owner, Resources res) {
super(orig, owner, res);
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 500efdd84854..ea9ba325a826 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -17,73 +17,125 @@
package android.graphics.drawable;
import android.annotation.ColorInt;
-import android.annotation.NonNull;
import android.graphics.Color;
import android.graphics.RuntimeShader;
import android.graphics.Shader;
final class RippleShader extends RuntimeShader {
- private static final String SHADER = "uniform float2 in_origin;\n"
- + "uniform float in_maxRadius;\n"
+ private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n"
+ + "uniform vec2 in_touch;\n"
+ "uniform float in_progress;\n"
+ + "uniform float in_maxRadius;\n"
+ + "uniform vec2 in_resolution;\n"
+ "uniform float in_hasMask;\n"
- + "uniform float4 in_color;\n"
- + "uniform shader in_shader;\n"
- + "float dist2(float2 p0, float2 pf) { return sqrt((pf.x - p0.x) * (pf.x - p0.x) + "
- + "(pf.y - p0.y) * (pf.y - p0.y)); }\n"
- + "float mod2(float a, float b) { return a - (b * floor(a / b)); }\n"
- + "float rand(float2 src) { return fract(sin(dot(src.xy, float2(12.9898, 78.233))) * "
- + "43758.5453123); }\n"
- + "float4 main(float2 p)\n"
- + "{\n"
- + " float fraction = in_progress;\n"
- + " float2 fragCoord = p;//sk_FragCoord.xy;\n"
- + " float maxDist = in_maxRadius;\n"
- + " float fragDist = dist2(in_origin, fragCoord.xy);\n"
- + " float circleRadius = maxDist * fraction;\n"
- + " float colorVal = (fragDist - circleRadius) / maxDist;\n"
- + " float d = fragDist < circleRadius \n"
- + " ? 1. - abs(colorVal * 3. * smoothstep(0., 1., fraction)) \n"
- + " : 1. - abs(colorVal * 5.);\n"
- + " d = smoothstep(0., 1., d);\n"
- + " float divider = 2.;\n"
- + " float x = floor(fragCoord.x / divider);\n"
- + " float y = floor(fragCoord.y / divider);\n"
- + " float density = .95;\n"
- + " d = rand(float2(x, y)) > density ? d : d * .2;\n"
- + " d = d * rand(float2(fraction, x * y));\n"
- + " float alpha = 1. - pow(fraction, 2.);\n"
- + " if (in_hasMask != 0.) {return sample(in_shader).a * in_color * d * alpha;}\n"
- + " return in_color * d * alpha;\n"
+ + "uniform float in_noisePhase;\n"
+ + "uniform vec4 in_color;\n"
+ + "uniform shader in_shader;\n";
+ private static final String SHADER_LIB =
+ "float triangleNoise(vec2 n) {\n"
+ + " n = fract(n * vec2(5.3987, 5.4421));\n"
+ + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n"
+ + " float xy = n.x * n.y;\n"
+ + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ + "}"
+ + "const float PI = 3.1415926535897932384626;\n"
+ + "\n"
+ + "float threshold(float v, float l, float h) {\n"
+ + " return step(l, v) * (1.0 - step(h, v));\n"
+ + "}\n"
+ + "\n"
+ + "float sparkles(vec2 uv, float t) {\n"
+ + " float n = triangleNoise(uv);\n"
+ + " float s = 0.0;\n"
+ + " for (float i = 0; i < 4; i += 1) {\n"
+ + " float l = i * 0.25;\n"
+ + " float h = l + 0.005;\n"
+ + " float o = abs(sin(0.1 * PI * (t + i)));\n"
+ + " s += threshold(n + o, l, h);\n"
+ + " }\n"
+ + " return saturate(s);\n"
+ + "}\n"
+ + "\n"
+ + "float softCircle(vec2 uv, vec2 xy, float radius, float blur) {\n"
+ + " float blurHalf = blur * 0.5;\n"
+ + " float d = distance(uv, xy);\n"
+ + " return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n"
+ + "}\n"
+ + "\n"
+ + "float getRingMask(vec2 frag, vec2 center, float r, float progress) {\n"
+ + " float dist = distance(frag, center);\n"
+ + " float expansion = r * .6;\n"
+ + " r = r * min(1.,progress);\n"
+ + " float minD = max(r - expansion, 0.);\n"
+ + " float maxD = r + expansion;\n"
+ + " if (dist > maxD || dist < minD) return .0;\n"
+ + " return min(maxD - dist, dist - minD) / expansion; \n"
+ + "}\n"
+ + "\n"
+ + "float subProgress(float start, float end, float progress) {\n"
+ + " float sub = clamp(progress, start, end);\n"
+ + " return (sub - start) / (end - start); \n"
+ "}\n";
+ private static final String SHADER_MAIN = "vec4 main(vec2 p) {\n"
+ + " float fadeIn = subProgress(0., 0.175, in_progress);\n"
+ + " float fadeOutNoise = subProgress(0.375, 1., in_progress);\n"
+ + " float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n"
+ + " vec2 center = mix(in_touch, in_origin, fadeIn);\n"
+ + " float ring = getRingMask(p, center, in_maxRadius, fadeIn);\n"
+ + " float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
+ + " float sparkle = sparkles(p, in_noisePhase) * ring * alpha;\n"
+ + " float fade = min(fadeIn, 1. - fadeOutRipple);\n"
+ + " vec4 circle = in_color * (softCircle(p, center, in_maxRadius "
+ + " * fadeIn, 0.2) * fade);\n"
+ + " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
+ + " return mix(circle, vec4(sparkle), sparkle) * mask;\n"
+ + "}";
+ private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
RippleShader() {
super(SHADER, false);
}
- public void setShader(@NonNull Shader s) {
- setInputShader("in_shader", s);
+ public void setShader(Shader shader) {
+ if (shader != null) {
+ setInputShader("in_shader", shader);
+ }
+ setUniform("in_hasMask", shader == null ? 0 : 1);
}
public void setRadius(float radius) {
setUniform("in_maxRadius", radius);
}
+ /**
+ * Continuous offset used as noise phase.
+ */
+ public void setNoisePhase(float t) {
+ setUniform("in_noisePhase", t);
+ }
+
public void setOrigin(float x, float y) {
setUniform("in_origin", new float[] {x, y});
}
- public void setProgress(float progress) {
- setUniform("in_progress", progress);
+ public void setTouch(float x, float y) {
+ setUniform("in_touch", new float[] {x, y});
}
- public void setHasMask(boolean hasMask) {
- setUniform("in_hasMask", hasMask ? 1 : 0);
+ public void setProgress(float progress) {
+ setUniform("in_progress", progress);
}
+ /**
+ * Color of the circle that's under the sparkles. Sparkles will always be white.
+ */
public void setColor(@ColorInt int colorIn) {
Color color = Color.valueOf(colorIn);
this.setUniform("in_color", new float[] {color.red(),
color.green(), color.blue(), color.alpha()});
}
+
+ public void setResolution(float w, float h) {
+ setUniform("in_resolution", w, h);
+ }
}
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index af49619fb840..917eef2ffede 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -183,6 +183,23 @@ public class FontFileUtil {
return nIsPostScriptType1Font(buffer, index);
}
+ /**
+ * Analyze the file content and returns 1 if the font file is an OpenType collection file, 0 if
+ * the font file is a OpenType font file, -1 otherwise.
+ */
+ public static int isCollectionFont(@NonNull ByteBuffer buffer) {
+ ByteBuffer copied = buffer.slice();
+ copied.order(ByteOrder.BIG_ENDIAN);
+ int magicNumber = copied.getInt(0);
+ if (magicNumber == TTC_TAG) {
+ return 1;
+ } else if (magicNumber == SFNT_VERSION_1 || magicNumber == SFNT_VERSION_OTTO) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
@FastNative
private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
@IntRange(from = 0) int index);
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index ed789f03f9ba..35b1c169f283 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.security.usermanager.IKeystoreUserManager;
+import android.security.maintenance.IKeystoreMaintenance;
import android.system.keystore2.Domain;
import android.system.keystore2.ResponseCode;
import android.util.Log;
@@ -34,9 +34,9 @@ public class AndroidKeyStoreMaintenance {
public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
- private static IKeystoreUserManager getService() {
- return IKeystoreUserManager.Stub.asInterface(
- ServiceManager.checkService("android.security.usermanager"));
+ private static IKeystoreMaintenance getService() {
+ return IKeystoreMaintenance.Stub.asInterface(
+ ServiceManager.checkService("android.security.maintenance"));
}
/**
@@ -121,4 +121,22 @@ public class AndroidKeyStoreMaintenance {
return SYSTEM_ERROR;
}
}
+
+ /**
+ * Queries user state from Keystore 2.0.
+ *
+ * @param userId - Android user id of the user.
+ * @return UserState enum variant as integer if successful or an error
+ */
+ public static int getState(int userId) {
+ try {
+ return getService().getState(userId);
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "getState failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 11cb2b7c724b..7c80f70593df 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -601,7 +601,7 @@ public final class KeyChain {
}
/**
- * Check whether the caller is the credential management app {@link CredentialManagementApp}.
+ * Check whether the caller is the credential management app {@code CredentialManagementApp}.
* The credential management app has the ability to manage the user's KeyChain credentials
* on unmanaged devices.
*
@@ -611,6 +611,7 @@ public final class KeyChain {
*
* @return {@code true} if the caller is the credential management app.
*/
+ @WorkerThread
public static boolean isCredentialManagementApp(@NonNull Context context) {
boolean isCredentialManagementApp = false;
try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
@@ -634,6 +635,7 @@ public final class KeyChain {
* @return the credential management app's authentication policy.
* @throws SecurityException if the caller is not the credential management app.
*/
+ @WorkerThread
@NonNull
public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy(
@NonNull Context context) throws SecurityException {
@@ -665,6 +667,7 @@ public final class KeyChain {
* @hide
*/
@TestApi
+ @WorkerThread
@RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
public static boolean setCredentialManagementApp(@NonNull Context context,
@NonNull String packageName, @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
@@ -680,13 +683,21 @@ public final class KeyChain {
}
/**
- * Remove the user's KeyChain credentials on unmanaged devices.
+ * Called by the credential management app {@code CredentialManagementApp} to unregister as
+ * the credential management app and stop managing the user's credentials.
+ *
+ * <p> All credentials previously installed by the credential management app will be removed
+ * from the user's device.
+ *
+ * <p> An app holding {@code MANAGE_CREDENTIAL_MANAGEMENT_APP} permission can also call this
+ * method to remove the current credential management app, even if it's not the current
+ * credential management app itself.
*
* @return {@code true} if the credential management app was successfully removed.
- * @hide
*/
- @TestApi
- @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
+ @WorkerThread
+ @RequiresPermission(value = Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
+ conditional = true)
public static boolean removeCredentialManagementApp(@NonNull Context context) {
try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
keyChainConnection.getService().removeCredentialManagementApp();
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 93658e69eac8..a08f390c9fd3 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeystoreResponse;
import android.security.keystore.UserNotAuthenticatedException;
+import android.security.maintenance.UserState;
import android.system.keystore2.Domain;
import android.util.Log;
@@ -196,6 +197,19 @@ public class KeyStore {
public State state(int userId) {
final int ret;
try {
+ if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+ int userState = AndroidKeyStoreMaintenance.getState(userId);
+ switch (userState) {
+ case UserState.UNINITIALIZED:
+ return KeyStore.State.UNINITIALIZED;
+ case UserState.LSKF_UNLOCKED:
+ return KeyStore.State.UNLOCKED;
+ case UserState.LSKF_LOCKED:
+ return KeyStore.State.LOCKED;
+ default:
+ throw new AssertionError(userState);
+ }
+ }
ret = mBinder.getState(userId);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index f48da74eb397..cd77d9c2723f 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -22,7 +22,6 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Build;
-import android.security.KeyStore;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
@@ -32,9 +31,14 @@ import android.util.ArraySet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.spec.ECGenParameterSpec;
import java.util.Collection;
+import java.util.Random;
import java.util.Set;
/**
@@ -223,22 +227,47 @@ public abstract class AttestationUtils {
@NonNull public static X509Certificate[] attestDeviceIds(Context context,
@NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
DeviceIdAttestationException {
- final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId(
- context, idTypes, attestationChallenge);
-
- // Perform attestation.
- final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
- final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new DeviceIdAttestationException("Unable to perform attestation",
- KeyStore.getKeyStoreException(errorCode));
+ String keystoreAlias = generateRandomAlias();
+ KeyGenParameterSpec.Builder builder =
+ new KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_SIGN)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setAttestationChallenge(attestationChallenge);
+
+ if (idTypes != null) {
+ builder.setAttestationIds(idTypes);
+ builder.setDevicePropertiesAttestationIncluded(true);
}
try {
- return parseCertificateChain(outChain);
- } catch (KeyAttestationException e) {
- throw new DeviceIdAttestationException(e.getMessage(), e);
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+ keyPairGenerator.initialize(builder.build());
+ keyPairGenerator.generateKeyPair();
+
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+
+ X509Certificate[] certificateChain =
+ (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias);
+
+ keyStore.deleteEntry(keystoreAlias);
+
+ return certificateChain;
+ } catch (Exception e) {
+ throw new DeviceIdAttestationException("Unable to perform attestation", e);
+ }
+ }
+
+ private static String generateRandomAlias() {
+ Random random = new SecureRandom();
+ StringBuilder builder = new StringBuilder();
+ // Pick random uppercase letters, A-Z. 20 of them gives us ~94 bits of entropy, which
+ // should prevent any conflicts with app-selected aliases, even for very unlucky users.
+ for (int i = 0; i < 20; ++i) {
+ builder.append(random.nextInt(26) + 'A');
}
+ return builder.toString();
}
/**
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 72735a787b7f..5cb2c3b41517 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -467,8 +467,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
*
* @return The numeric namespace as configured in the keystore2_key_contexts files of Android's
* SEPolicy.
- * TODO b/171806779 link to public Keystore 2.0 documentation.
- * See bug for more details for now.
+ * See <a href="https://source.android.com/security/keystore#access-control">
+ * Keystore 2.0 access control</a>
* @hide
*/
@SystemApi
@@ -1042,9 +1042,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* keys between system and vendor components, e.g., WIFI settings and WPA supplicant.
*
* @param namespace Numeric SELinux namespace as configured in keystore2_key_contexts
- * of Android's SEPolicy.
- * TODO b/171806779 link to public Keystore 2.0 documentation.
- * See bug for more details for now.
+ * of Android's SEPolicy.
+ * See <a href="https://source.android.com/security/keystore#access-control">
+ * Keystore 2.0 access control</a>
* @return this Builder object.
*
* @hide
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d36695b9b410..fa852e33a1d8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -340,11 +340,11 @@ public class AndroidKeyStoreProvider extends Provider {
* @param keyStore The keystore2 backend.
* @param alias The alias of the key in the Keystore database.
* @param namespace The a Keystore namespace. This is used by system api only to request
- * Android system specific keystore namespace, which can be configured
- * in the device's SEPolicy. Third party apps and most system components
- * set this parameter to -1 to indicate their application specific namespace.
- * TODO b/171806779 link to public Keystore 2.0 documentation.
- * See bug for more details for now.
+ * Android system specific keystore namespace, which can be configured
+ * in the device's SEPolicy. Third party apps and most system components
+ * set this parameter to -1 to indicate their application specific namespace.
+ * See <a href="https://source.android.com/security/keystore#access-control">
+ * Keystore 2.0 access control</a>
* @hide
**/
@NonNull
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index c0bc73dcbd47..d2b3cf6a4fe2 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -18,4 +18,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell">
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
</manifest>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 24198659e15d..c2f591b9d7af 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -51,4 +51,10 @@
<!-- maximum animation duration for the icon when entering the starting window -->
<integer name="max_starting_window_intro_icon_anim_duration">1000</integer>
+
+ <!-- Animation duration when exit starting window: icon going away -->
+ <integer name="starting_window_icon_exit_anim_duration">166</integer>
+
+ <!-- Animation duration when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_duration">333</integer>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 75bed3777a9d..3ced8d3ec6e7 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -188,4 +188,13 @@
<!-- The height of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_height">80dp</dimen>
+
+ <!-- The length of the shift of main window when exit starting window. -->
+ <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
+
+ <!-- The distance of the shift icon when normal exit starting window. -->
+ <dimen name="starting_surface_normal_exit_icon_distance">120dp</dimen>
+
+ <!-- The distance of the shift icon when early exit starting window. -->
+ <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index cb54021d7a23..3708e151c372 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -119,7 +119,7 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
listeners.get(i).onDisplayAreaAppeared(displayAreaInfo);
}
}
- applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+ applyConfigChangesToContext(displayAreaInfo);
}
@Override
@@ -161,24 +161,27 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
listeners.get(i).onDisplayAreaInfoChanged(displayAreaInfo);
}
}
- applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+ applyConfigChangesToContext(displayAreaInfo);
}
/**
- * Applies the {@link Configuration} to the {@link DisplayAreaContext} specified by
- * {@code displayId}.
- *
- * @param displayId The ID of the {@link Display} which the {@link DisplayAreaContext} is
- * associated with
- * @param newConfig The propagated configuration
+ * Applies the {@link DisplayAreaInfo} to the {@link DisplayAreaContext} specified by
+ * {@link DisplayAreaInfo#displayId}.
*/
- private void applyConfigChangesToContext(int displayId, @NonNull Configuration newConfig) {
+ private void applyConfigChangesToContext(@NonNull DisplayAreaInfo displayAreaInfo) {
+ final int displayId = displayAreaInfo.displayId;
+ final Display display = mContext.getSystemService(DisplayManager.class)
+ .getDisplay(displayId);
+ if (display == null) {
+ throw new UnsupportedOperationException("The display #" + displayId + " is invalid."
+ + "displayAreaInfo:" + displayAreaInfo);
+ }
DisplayAreaContext daContext = mDisplayAreaContexts.get(displayId);
if (daContext == null) {
- daContext = new DisplayAreaContext(mContext, displayId);
+ daContext = new DisplayAreaContext(mContext, display);
mDisplayAreaContexts.put(displayId, daContext);
}
- daContext.updateConfigurationChanges(newConfig);
+ daContext.updateConfigurationChanges(displayAreaInfo.configuration);
}
/**
@@ -228,10 +231,8 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
private final IBinder mToken = new Binder();
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
- public DisplayAreaContext(@NonNull Context context, int displayId) {
+ public DisplayAreaContext(@NonNull Context context, @NonNull Display display) {
super(null);
- final Display display = context.getSystemService(DisplayManager.class)
- .getDisplay(displayId);
attachBaseContext(context.createTokenContext(mToken, display));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index cb04bd7ce02b..fcb53cd890b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,6 +31,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.LocusId;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
@@ -307,9 +308,10 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
@Override
- public void removeStartingWindow(int taskId) {
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
if (mStartingSurface != null) {
- mStartingSurface.removeStartingWindow(taskId);
+ mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 46884fefd69c..7d65a08ce798 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -83,6 +83,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private boolean mIsInitialized;
private Listener mListener;
private Executor mListenerExecutor;
+ private Rect mObscuredTouchRect;
private final Rect mTmpRect = new Rect();
private final Rect mTmpRootRect = new Rect();
@@ -161,6 +162,15 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
}
/**
+ * Indicates a region of the view that is not touchable.
+ *
+ * @param obscuredRect the obscured region of the view.
+ */
+ public void setObscuredTouchRect(Rect obscuredRect) {
+ mObscuredTouchRect = obscuredRect;
+ }
+
+ /**
* Call when view position or size has changed. Do not call when animating.
*/
public void onLocationChanged() {
@@ -384,6 +394,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+
+ if (mObscuredTouchRect != null) {
+ inoutInfo.touchableRegion.union(mObscuredTouchRect);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 562b32b41dd2..b6d408afd703 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
@@ -88,7 +89,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
task1.taskId, task2.taskId, this);
- if (!task1.isResizeable || !task2.isResizeable) {
+ if ((!task1.isResizeable || !task2.isResizeable)
+ && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
ProtoLog.e(WM_SHELL_TASK_ORG,
"Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
task1.isResizeable, task2.isResizeable);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 64a44ca9e7e9..16ede735660f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -41,7 +41,6 @@ import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Region;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -1046,6 +1045,7 @@ public class BubbleStackView extends FrameLayout
}
};
+ // TODO: Create ManageMenuView and move setup / animations there
private void setUpManageMenu() {
if (mManageMenu != null) {
removeView(mManageMenu);
@@ -2146,50 +2146,6 @@ public class BubbleStackView extends FrameLayout
}
/**
- * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a
- * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV).
- * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided
- * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to
- * the special nature of ActivityView, it does not respect the standard
- * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for
- * this purpose.
- *
- * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation
- * properties for performance reasons. This means that the default implementation of this method
- * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in
- * it not receiving any touch events. This was previously addressed by returning false in the
- * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any
- * touch handlers in the stack or its child views.
- *
- * To support touch handlers, we're overriding this method to leave the ActivityView's touchable
- * region alone. The only touchable part of the stack that can ever overlap the AV is a
- * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually
- * updating the touchable region to allow users to grab a bubble while it completes its ~50ms
- * animation back to the bubble row.
- *
- * NOTE: Any future additions to the stack that obscure the ActivityView region will need their
- * bounds subtracted here in order to receive touch events.
- */
- @Override
- public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
- // If the notification shade is expanded, or the manage menu is open, or we are showing
- // manage bubbles user education, we shouldn't let the ActivityView steal any touch events
- // from any location.
- if (!mIsExpanded
- || mShowingManage
- || (mManageEduView != null
- && mManageEduView.getVisibility() == VISIBLE)) {
- touchableRegion.setEmpty();
- }
- }
-
- /**
- * If you're here because you're not receiving touch events on a view that is a descendant of
- * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the
- * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView
- * consumes all touch events within its bounds, even for views like the BubbleStackView that are
- * above it. It ignores typical view touch handling methods like this one and
- * dispatchTouchEvent.
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -2539,6 +2495,11 @@ public class BubbleStackView extends FrameLayout
}
mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+ if (mExpandedBubble.getExpandedView().getTaskView() != null) {
+ mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
+ ? new Rect(0, 0, getWidth(), getHeight())
+ : null);
+ }
final boolean isLtr =
getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 4bb8e9b6581f..9dabec7a13d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -32,7 +32,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DragEvent;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
@@ -369,9 +369,9 @@ public class SystemWindows {
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
try {
- callbacks.onScrollCaptureResponse(
+ listener.onScrollCaptureResponse(
new ScrollCaptureResponse.Builder()
.setDescription("Not Implemented")
.build());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 17709438baba..58bf22ad29b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -85,7 +85,8 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
@Override
public void onDisplayAdded(int displayId) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display added: %d", displayId);
- final Context context = mDisplayController.getDisplayContext(displayId);
+ final Context context = mDisplayController.getDisplayContext(displayId)
+ .createWindowContext(TYPE_APPLICATION_OVERLAY, null);
final WindowManager wm = context.getSystemService(WindowManager.class);
// TODO(b/169894807): Figure out the right layer for this, needs to be below the task bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
index 7ce9014fc9ba..57a9dd2ec6cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
@@ -143,14 +143,14 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
@ImeAnimationFlags
public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
- mHiddenTop = hiddenTop;
- mShownTop = shownTop;
- mTargetShown = imeShouldShow;
if (!isDividerVisible()) {
return 0;
}
- final boolean splitIsVisible = !getView().isHidden();
+ mHiddenTop = hiddenTop;
+ mShownTop = shownTop;
+ mTargetShown = imeShouldShow;
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
+ final boolean splitIsVisible = !getView().isHidden();
final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
&& !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
&& !mSplits.mSplitScreenController.isMinimized();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
index eea5c08818cc..d06064a82ff0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
@@ -30,6 +30,7 @@ import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.os.IBinder;
@@ -91,9 +92,11 @@ public class SplitScreenTransitions implements Transitions.TransitionHandler {
// is nothing behind it.
((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
&& triggerTask.parentTaskId == mListener.mPrimary.taskId)
- // if a non-resizable is launched, we also need to leave split-screen.
+ // if a non-resizable is launched when it is not supported in multi window,
+ // we also need to leave split-screen.
|| ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && !triggerTask.isResizeable);
+ && !triggerTask.isResizeable
+ && !ActivityTaskManager.supportsNonResizableMultiWindow());
// In both cases, dismiss the primary
if (shouldDismiss) {
WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 82468ad999b4..5a2ef568d82a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,6 +46,7 @@ import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BooleanSupplier;
/**
* Proxy to simplify calls into window manager/activity manager
@@ -208,11 +209,17 @@ class WindowManagerProxy {
return false;
}
ActivityManager.RunningTaskInfo topHomeTask = null;
+ // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
+ // because the value can be changed at runtime.
+ final BooleanSupplier supportsNonResizableMultiWindow =
+ createSupportsNonResizableMultiWindowSupplier();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
- // Only move resizeable task to split secondary. However, we have an exception
- // for non-resizable home because we will minimize to show it.
- if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
+ // Check whether to move resizeable task to split secondary.
+ // Also, we have an exception for non-resizable home because we will minimize to show
+ // it.
+ if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
+ && !supportsNonResizableMultiWindow.getAsBoolean()) {
continue;
}
// Only move fullscreen tasks to split secondary.
@@ -357,6 +364,21 @@ class WindowManagerProxy {
outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
}
+ /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
+ private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
+ return new BooleanSupplier() {
+ private Boolean mSupportsNonResizableMultiWindow;
+ @Override
+ public boolean getAsBoolean() {
+ if (mSupportsNonResizableMultiWindow == null) {
+ mSupportsNonResizableMultiWindow =
+ ActivityTaskManager.supportsNonResizableMultiWindow();
+ }
+ return mSupportsNonResizableMultiWindow;
+ }
+ };
+ }
+
/**
* Utility to apply a sync transaction serially with other sync transactions.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 5fc7c987899f..c1a93ce753a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -73,6 +73,7 @@ public class OneHandedController {
private final Context mContext;
private final DisplayController mDisplayController;
private final OneHandedGestureHandler mGestureHandler;
+ private final OneHandedSettingsUtil mOneHandedSettingsUtil;
private final OneHandedTimeoutHandler mTimeoutHandler;
private final OneHandedTouchHandler mTouchHandler;
private final OneHandedTutorialHandler mTutorialHandler;
@@ -108,8 +109,12 @@ public class OneHandedController {
new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean enabled) {
+ if (mOneHandedSettingsUtil == null) {
+ Slog.w(TAG, "mOneHandedSettingsUtil may not instantiate yet");
+ return;
+ }
if (enabled) {
- final int mOneHandedTimeout = OneHandedSettingsUtil
+ final int mOneHandedTimeout = mOneHandedSettingsUtil
.getSettingsOneHandedModeTimeout(mContext.getContentResolver());
final int timeout = mAccessibilityManager
.getRecommendedTimeoutMillis(mOneHandedTimeout * 1000
@@ -117,7 +122,7 @@ public class OneHandedController {
AccessibilityManager.FLAG_CONTENT_CONTROLS);
mTimeoutHandler.setTimeout(timeout / 1000);
} else {
- mTimeoutHandler.setTimeout(OneHandedSettingsUtil
+ mTimeoutHandler.setTimeout(mOneHandedSettingsUtil
.getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
}
}
@@ -164,15 +169,16 @@ public class OneHandedController {
new OneHandedBackgroundPanelOrganizer(context, windowManager, displayController,
mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
- context, windowManager, displayController, animationController, tutorialHandler,
+ context, windowManager, animationController, tutorialHandler,
oneHandedBackgroundPanelOrganizer, mainExecutor);
+ OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, windowManager, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- gestureHandler, timeoutHandler, oneHandedUiEventsLogger, overlayManager,
- taskStackListener, mainExecutor, mainHandler);
+ gestureHandler, settingsUtil, timeoutHandler, oneHandedUiEventsLogger,
+ overlayManager, taskStackListener, mainExecutor, mainHandler);
}
@VisibleForTesting
@@ -184,6 +190,7 @@ public class OneHandedController {
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
OneHandedGestureHandler gestureHandler,
+ OneHandedSettingsUtil settingsUtil,
OneHandedTimeoutHandler timeoutHandler,
OneHandedUiEventLogger uiEventsLogger,
IOverlayManager overlayManager,
@@ -191,6 +198,7 @@ public class OneHandedController {
ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
+ mOneHandedSettingsUtil = settingsUtil;
mWindowManager = windowManager;
mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
@@ -209,10 +217,10 @@ public class OneHandedController {
final int sysPropPercentageConfig = SystemProperties.getInt(
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
mOffSetFraction = sysPropPercentageConfig / 100.0f;
- mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ mIsOneHandedEnabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
context.getContentResolver());
mIsSwipeToNotificationEnabled =
- OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
context.getContentResolver());
mTimeoutHandler = timeoutHandler;
@@ -325,25 +333,25 @@ public class OneHandedController {
}
private void setupSettingObservers() {
- OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
mContext.getContentResolver(), mEnabledObserver);
- OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
mContext.getContentResolver(), mTimeoutObserver);
- OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
mContext.getContentResolver(), mTaskChangeExitObserver);
- OneHandedSettingsUtil.registerSettingsKeyObserver(
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
mContext.getContentResolver(), mSwipeToNotificationEnabledObserver);
}
private void updateSettings() {
- setOneHandedEnabled(OneHandedSettingsUtil
+ setOneHandedEnabled(mOneHandedSettingsUtil
.getSettingsOneHandedModeEnabled(mContext.getContentResolver()));
- mTimeoutHandler.setTimeout(OneHandedSettingsUtil
+ mTimeoutHandler.setTimeout(mOneHandedSettingsUtil
.getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
- setTaskChangeToExit(OneHandedSettingsUtil
+ setTaskChangeToExit(mOneHandedSettingsUtil
.getSettingsTapsAppToExit(mContext.getContentResolver()));
- setSwipeToNotificationEnabled(OneHandedSettingsUtil
+ setSwipeToNotificationEnabled(mOneHandedSettingsUtil
.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
}
@@ -358,7 +366,7 @@ public class OneHandedController {
@VisibleForTesting
void onEnabledSettingChanged() {
- final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver());
mOneHandedUiEventLogger.writeEvent(enabled
? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
@@ -368,13 +376,13 @@ public class OneHandedController {
// Also checks swipe to notification settings since they all need gesture overlay.
setEnabledGesturalOverlay(
- enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver()));
}
@VisibleForTesting
void onTimeoutSettingChanged() {
- final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
+ final int newTimeout = mOneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
mContext.getContentResolver());
int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId();
switch (newTimeout) {
@@ -403,7 +411,7 @@ public class OneHandedController {
@VisibleForTesting
void onTaskChangeExitSettingChanged() {
- final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
+ final boolean enabled = mOneHandedSettingsUtil.getSettingsTapsAppToExit(
mContext.getContentResolver());
mOneHandedUiEventLogger.writeEvent(enabled
? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
@@ -415,13 +423,13 @@ public class OneHandedController {
@VisibleForTesting
void onSwipeToNotificationEnabledSettingChanged() {
final boolean enabled =
- OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver());
setSwipeToNotificationEnabled(enabled);
// Also checks one handed mode settings since they all need gesture overlay.
setEnabledGesturalOverlay(
- enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver()));
}
@@ -480,7 +488,8 @@ public class OneHandedController {
}
private void setupGesturalOverlay() {
- if (!OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())) {
+ if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ mContext.getContentResolver())) {
return;
}
@@ -551,7 +560,7 @@ public class OneHandedController {
mTutorialHandler.dump(pw);
}
- OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver());
+ mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver());
if (mOverlayManager != null) {
OverlayInfo info = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 0238fa8a7936..5eec23106f47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -37,7 +37,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -67,7 +66,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private int mEnterExitAnimationDurationMs;
private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
- private DisplayController mDisplayController;
private OneHandedAnimationController mAnimationController;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -111,7 +109,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
*/
public OneHandedDisplayAreaOrganizer(Context context,
WindowManager windowManager,
- DisplayController displayController,
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
@@ -119,7 +116,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
super(mainExecutor);
mWindowManager = windowManager;
mAnimationController = animationController;
- mDisplayController = displayController;
mLastVisualDisplayBounds.set(getDisplayBounds());
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
@@ -171,10 +167,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
*/
public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) {
// Stop one handed without animation and reset cropped size immediately
- final Rect newBounds = new Rect(mDefaultDisplayBounds);
+ final Rect newBounds = new Rect(getDisplayBounds());
+ // This diff rule will only filter the cases portrait <-> landscape
final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
if (isOrientationDiff) {
+ // getDisplayBounds() will return window metrics bounds which dose not update to
+ // corresponding display orientation yet, we have to manual rotate bounds
+ newBounds.set(0, 0, newBounds.bottom, newBounds.right);
resetWindowsOffset(wct);
mDefaultDisplayBounds.set(newBounds);
mLastVisualDisplayBounds.set(newBounds);
@@ -293,7 +293,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
}
@Nullable
- private Rect getDisplayBounds() {
+ @VisibleForTesting
+ Rect getDisplayBounds() {
if (mWindowManager == null) {
Slog.e(TAG, "WindowManager instance is null! Can not get display size!");
return new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index f8217c64e53d..4768cf58152b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -67,7 +67,7 @@ public final class OneHandedSettingsUtil {
* @param observer Observer from caller
* @return uri key for observing
*/
- public static Uri registerSettingsKeyObserver(String key, ContentResolver resolver,
+ public Uri registerSettingsKeyObserver(String key, ContentResolver resolver,
ContentObserver observer) {
Uri uriKey = null;
uriKey = Settings.Secure.getUriFor(key);
@@ -83,7 +83,7 @@ public final class OneHandedSettingsUtil {
* @param resolver ContentResolver of context
* @param observer preference key change observer
*/
- public static void unregisterSettingsKeyObserver(ContentResolver resolver,
+ public void unregisterSettingsKeyObserver(ContentResolver resolver,
ContentObserver observer) {
if (resolver != null) {
resolver.unregisterContentObserver(observer);
@@ -95,7 +95,7 @@ public final class OneHandedSettingsUtil {
*
* @return enable or disable one handed mode flag.
*/
- public static boolean getSettingsOneHandedModeEnabled(ContentResolver resolver) {
+ public boolean getSettingsOneHandedModeEnabled(ContentResolver resolver) {
return Settings.Secure.getInt(resolver,
Settings.Secure.ONE_HANDED_MODE_ENABLED, 0 /* Disabled */) == 1;
}
@@ -105,7 +105,7 @@ public final class OneHandedSettingsUtil {
*
* @return enable or disable taps app exit.
*/
- public static boolean getSettingsTapsAppToExit(ContentResolver resolver) {
+ public boolean getSettingsTapsAppToExit(ContentResolver resolver) {
return Settings.Secure.getInt(resolver,
Settings.Secure.TAPS_APP_TO_EXIT, 0) == 1;
}
@@ -116,7 +116,7 @@ public final class OneHandedSettingsUtil {
*
* @return timeout value in seconds.
*/
- public static @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver) {
+ public @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver) {
return Settings.Secure.getInt(resolver,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
}
@@ -124,12 +124,12 @@ public final class OneHandedSettingsUtil {
/**
* Returns whether swipe bottom to notification gesture enabled or not.
*/
- public static boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) {
+ public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) {
return Settings.Secure.getInt(resolver,
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1) == 1;
}
- protected static void dump(PrintWriter pw, String prefix, ContentResolver resolver) {
+ void dump(PrintWriter pw, String prefix, ContentResolver resolver) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
pw.print(innerPrefix + "isOneHandedModeEnable=");
@@ -139,6 +139,6 @@ public final class OneHandedSettingsUtil {
pw.print(innerPrefix + "tapsAppToExit=");
pw.println(getSettingsTapsAppToExit(resolver));
}
-
- private OneHandedSettingsUtil() {}
+ public OneHandedSettingsUtil() {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9ec7c0d173dd..36dc4e409f98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -41,6 +41,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
@@ -423,12 +424,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (mInSwipePipToHomeTransition) {
final Rect destinationBounds = mPipBoundsState.getBounds();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds);
+ mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds);
// animation is finished in the Launcher and here we directly apply the final touch.
applyEnterPipSyncTransaction(destinationBounds, () -> {
// ensure menu's settled in its final bounds first
finishResizeForMenu(destinationBounds);
sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- });
+ }, tx);
mInSwipePipToHomeTransition = false;
return;
}
@@ -490,16 +495,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition.
mState = State.ENTERING_PIP;
- });
+ }, null /* boundsChangeTransaction */);
}
- private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
+ private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable,
+ @Nullable SurfaceControl.Transaction boundsChangeTransaction) {
// PiP menu is attached late in the process here to avoid any artifacts on the leash
// caused by addShellRoot when in gesture navigation mode.
mPipMenuController.attach(mLeash);
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.setBounds(mToken, destinationBounds);
+ if (boundsChangeTransaction != null) {
+ wct.setBoundsChangeTransaction(mToken, boundsChangeTransaction);
+ }
wct.scheduleFinishEnterPip(mToken, destinationBounds);
mSyncTransactionQueue.queue(wct);
if (runnable != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 725f87d93e4e..580861cf4974 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -121,6 +121,19 @@ public class PhonePipMenuController implements PipMenuController {
}
};
+ private final float[] mTmpValues = new float[9];
+ private final Runnable mUpdateEmbeddedMatrix = () -> {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ return;
+ }
+ mMoveTransform.getValues(mTmpValues);
+ try {
+ mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection()
+ .setScreenMatrix(mTmpValues);
+ } catch (RemoteException e) {
+ }
+ };
+
public PhonePipMenuController(Context context, PipMediaController mediaController,
SystemWindows systemWindows, ShellExecutor mainExecutor,
Handler mainHandler) {
@@ -306,6 +319,11 @@ public class PhonePipMenuController implements PipMenuController {
} else {
mApplier.scheduleApply(params);
}
+
+ if (mPipMenuView.getViewRootImpl() != null) {
+ mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
+ mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 0d64407f649f..8ab405bca7db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -32,7 +32,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.annotation.Nullable;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
import android.content.ComponentName;
@@ -52,10 +51,8 @@ import android.util.Size;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -198,6 +195,11 @@ public class PipMenuView extends FrameLayout {
}
@Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!mAllowTouches) {
return false;
@@ -287,18 +289,6 @@ public class PipMenuView extends FrameLayout {
}
}
- @Nullable SurfaceControl getWindowSurfaceControl() {
- final ViewRootImpl root = getViewRootImpl();
- if (root == null) {
- return null;
- }
- final SurfaceControl out = root.getSurfaceControl();
- if (out != null && out.isValid()) {
- return out;
- }
- return null;
- }
-
/**
* Different from {@link #hideMenu()}, this function does not try to finish this menu activity
* and instead, it fades out the controls by setting the alpha to 0 directly without menu
@@ -398,8 +388,20 @@ public class PipMenuView extends FrameLayout {
return true;
});
+ // Update the expand button only if it should show with the menu
+ expandContainer.setVisibility(mMenuState == MENU_STATE_FULL
+ ? View.VISIBLE
+ : View.INVISIBLE);
+
+ FrameLayout.LayoutParams expandedLp =
+ (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
+
+ // Update the expand container margin to adjust the center of the expand button to
+ // account for the existence of the action container
+ expandedLp.topMargin = 0;
+ expandedLp.bottomMargin = 0;
} else {
actionsContainer.setVisibility(View.VISIBLE);
if (mActionsGroup != null) {
@@ -455,14 +457,12 @@ public class PipMenuView extends FrameLayout {
// Update the expand container margin to adjust the center of the expand button to
// account for the existence of the action container
- FrameLayout.LayoutParams expandedLp =
- (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
expandedLp.topMargin = getResources().getDimensionPixelSize(
R.dimen.pip_action_padding);
expandedLp.bottomMargin = getResources().getDimensionPixelSize(
R.dimen.pip_expand_container_edge_margin);
- expandContainer.requestLayout();
}
+ expandContainer.requestLayout();
}
private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 543ecfcf1a33..44e262492b97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -73,6 +73,7 @@ public class PipTouchHandler {
// Allow PIP to resize to a slightly bigger state upon touch
private boolean mEnableResize;
private final Context mContext;
+ private final PipTaskOrganizer mPipTaskOrganizer;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
@@ -169,6 +170,7 @@ public class PipTouchHandler {
mContext = context;
mMainExecutor = mainExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
@@ -982,7 +984,9 @@ public class PipTouchHandler {
void setPipExclusionBoundsChangeListener(Consumer<Rect> pipExclusionBoundsChangeListener) {
mPipExclusionBoundsChangeListener = new WeakReference<>(pipExclusionBoundsChangeListener);
- pipExclusionBoundsChangeListener.accept(mPipBoundsState.getBounds());
+ pipExclusionBoundsChangeListener.accept(mPipTaskOrganizer.isInPip()
+ ? mPipBoundsState.getBounds() : new Rect());
+
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
new file mode 100644
index 000000000000..5bc2afd11fe8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.startingsurface;
+
+import static android.view.View.GONE;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateYAnimation;
+import android.window.SplashScreenView;
+
+import com.android.wm.shell.common.TransactionPool;
+
+/**
+ * Default animation for exiting the splash screen window.
+ * @hide
+ */
+public class SplashScreenExitAnimation implements Animator.AnimatorListener {
+ private static final boolean DEBUG_EXIT_ANIMATION = false;
+ private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+ private static final String TAG = StartingSurfaceDrawer.TAG;
+
+ private static final Interpolator ICON_EXIT_INTERPOLATOR = new PathInterpolator(1f, 0f, 1f, 1f);
+ private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
+
+ private static final int EXTRA_REVEAL_DELAY = 133;
+ private final Matrix mTmpTransform = new Matrix();
+ private final float[] mTmpFloat9 = new float[9];
+ private SurfaceControl mFirstWindowSurface;
+ private final Rect mFirstWindowFrame = new Rect();
+ private final SplashScreenView mSplashScreenView;
+ private final int mMainWindowShiftLength;
+ private final int mIconShiftLength;
+ private final int mAppDuration;
+ private final int mIconDuration;
+ private final TransactionPool mTransactionPool;
+
+ private ValueAnimator mMainAnimator;
+ private Animation mShiftUpAnimation;
+ private AnimationSet mIconAnimationSet;
+ private Runnable mFinishCallback;
+
+ SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame,
+ int appDuration, int iconDuration, int mainWindowShiftLength, int iconShiftLength,
+ TransactionPool pool, Runnable handleFinish) {
+ mSplashScreenView = view;
+ mFirstWindowSurface = leash;
+ if (frame != null) {
+ mFirstWindowFrame.set(frame);
+ }
+ mAppDuration = appDuration;
+ mIconDuration = iconDuration;
+ mMainWindowShiftLength = mainWindowShiftLength;
+ mIconShiftLength = iconShiftLength;
+ mFinishCallback = handleFinish;
+ mTransactionPool = pool;
+ }
+
+ void prepareAnimations() {
+ prepareRevealAnimation();
+ prepareShiftAnimation();
+ }
+
+ void startAnimations() {
+ if (mIconAnimationSet != null) {
+ mIconAnimationSet.start();
+ }
+ if (mMainAnimator != null) {
+ mMainAnimator.start();
+ }
+ if (mShiftUpAnimation != null) {
+ mShiftUpAnimation.start();
+ }
+ }
+
+ // reveal splash screen, shift up main window
+ private void prepareRevealAnimation() {
+ // splash screen
+ mMainAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mMainAnimator.setDuration(mAppDuration);
+ mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR);
+ mMainAnimator.addListener(this);
+
+ final int startDelay = mIconDuration + EXTRA_REVEAL_DELAY;
+ final float transparentRatio = 0.95f;
+ final int globalHeight = mSplashScreenView.getHeight();
+ final int verticalCircleCenter = 0;
+ final int finalVerticalLength = globalHeight - verticalCircleCenter;
+ final int halfWidth = mSplashScreenView.getWidth() / 2;
+ final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
+ Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
+ final RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(
+ mSplashScreenView, mMainAnimator);
+ radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
+ radialVanishAnimation.setRadius(0/* initRadius */, endRadius);
+ final int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT, Color.WHITE};
+ final float[] stops = {0f, transparentRatio, 1f};
+ radialVanishAnimation.setRadialPaintParam(colors, stops);
+ radialVanishAnimation.setReady();
+ mMainAnimator.setStartDelay(startDelay);
+
+ if (mFirstWindowSurface != null) {
+ // shift up main window
+ View occludeHoleView = new View(mSplashScreenView.getContext());
+ if (DEBUG_EXIT_ANIMATION_BLEND) {
+ occludeHoleView.setBackgroundColor(Color.BLUE);
+ } else {
+ occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor());
+ }
+ final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
+ mSplashScreenView.addView(occludeHoleView, params);
+
+ mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength);
+ mShiftUpAnimation.setDuration(mAppDuration);
+ mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR);
+ mShiftUpAnimation.setStartOffset(startDelay);
+
+ occludeHoleView.setAnimation(mShiftUpAnimation);
+ }
+ }
+
+ // shift down icon and branding view
+ private void prepareShiftAnimation() {
+ final View iconView = mSplashScreenView.getIconView();
+ if (iconView == null) {
+ return;
+ }
+ if (mIconShiftLength > 0) {
+ mIconAnimationSet = new AnimationSet(true /* shareInterpolator */);
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "first exit animation, shift length: " + mIconShiftLength);
+ }
+ mIconAnimationSet.addAnimation(new TranslateYAnimation(0, mIconShiftLength));
+ mIconAnimationSet.addAnimation(new AlphaAnimation(1, 0));
+ mIconAnimationSet.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "first exit animation finished");
+ }
+ iconView.post(() -> iconView.setVisibility(GONE));
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ // ignore
+ }
+ });
+ mIconAnimationSet.setDuration(mIconDuration);
+ mIconAnimationSet.setInterpolator(ICON_EXIT_INTERPOLATOR);
+ iconView.setAnimation(mIconAnimationSet);
+ final View brandingView = mSplashScreenView.getBrandingView();
+ if (brandingView != null) {
+ brandingView.setAnimation(mIconAnimationSet);
+ }
+ }
+ }
+
+ private static class RadialVanishAnimation extends View {
+ private SplashScreenView mView;
+ private int mInitRadius;
+ private int mFinishRadius;
+ private boolean mReady;
+
+ private final Point mCircleCenter = new Point();
+ private final Matrix mVanishMatrix = new Matrix();
+ private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ RadialVanishAnimation(SplashScreenView target, ValueAnimator animator) {
+ super(target.getContext());
+ mView = target;
+ animator.addUpdateListener((animation) -> {
+ if (mVanishPaint.getShader() == null) {
+ return;
+ }
+ final float value = (float) animation.getAnimatedValue();
+ final float scale = (mFinishRadius - mInitRadius) * value + mInitRadius;
+ mVanishMatrix.setScale(scale, scale);
+ mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
+ mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
+ mView.postInvalidate();
+ });
+ mView.addView(this);
+ }
+
+ void setRadius(int initRadius, int finishRadius) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
+ + " final " + finishRadius);
+ }
+ mInitRadius = initRadius;
+ mFinishRadius = finishRadius;
+ }
+
+ void setCircleCenter(int x, int y) {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
+ }
+ mCircleCenter.set(x, y);
+ }
+
+ void setRadialPaintParam(int[] colors, float[] stops) {
+ // setup gradient shader
+ final RadialGradient rShader =
+ new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
+ mVanishPaint.setShader(rShader);
+ if (!DEBUG_EXIT_ANIMATION_BLEND) {
+ mVanishPaint.setBlendMode(BlendMode.MODULATE);
+ }
+ }
+
+ void setReady() {
+ mReady = true;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mReady) {
+ canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
+ }
+ }
+ }
+
+ private final class ShiftUpAnimation extends TranslateYAnimation {
+ ShiftUpAnimation(float fromYDelta, float toYDelta) {
+ super(fromYDelta, toYDelta);
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ super.applyTransformation(interpolatedTime, t);
+
+ if (mFirstWindowSurface == null) {
+ return;
+ }
+ mTmpTransform.set(t.getMatrix());
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ mTmpTransform.postTranslate(mFirstWindowFrame.left,
+ mFirstWindowFrame.top + mMainWindowShiftLength);
+ tx.setMatrix(mFirstWindowSurface, mTmpTransform, mTmpFloat9);
+ // TODO set the vsyncId to ensure the transaction doesn't get applied too early.
+ // Additionally, do you want to have this synchronized with your view animations?
+ // If so, you'll need to use SyncRtSurfaceTransactionApplier
+ tx.apply();
+ mTransactionPool.release(tx);
+ }
+ }
+
+ private void reset() {
+ if (DEBUG_EXIT_ANIMATION) {
+ Slog.v(TAG, "vanish animation finished");
+ }
+ mSplashScreenView.post(() -> {
+ mSplashScreenView.setVisibility(GONE);
+ if (mFinishCallback != null) {
+ mFinishCallback.run();
+ mFinishCallback = null;
+ }
+ });
+ if (mFirstWindowSurface != null) {
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ tx.setWindowCrop(mFirstWindowSurface, null);
+ tx.apply();
+ mFirstWindowSurface.release();
+ mFirstWindowSurface = null;
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // ignore
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ reset();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ reset();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // ignore
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 2973b5080ae6..3f9c2717731a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -31,12 +31,14 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.Slog;
+import android.view.SurfaceControl;
import android.window.SplashScreenView;
import com.android.internal.R;
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.Quantizer;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.wm.shell.common.TransactionPool;
import java.util.List;
@@ -56,15 +58,25 @@ public class SplashscreenContentDrawer {
// also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
private final Context mContext;
- private final int mMaxIconAnimationDuration;
+ private final int mMaxAnimatableIconDuration;
private int mIconSize;
private int mBrandingImageWidth;
private int mBrandingImageHeight;
-
- SplashscreenContentDrawer(Context context, int maxIconAnimationDuration) {
+ private final int mAppRevealDuration;
+ private final int mIconExitDuration;
+ private int mMainWindowShiftLength;
+ private int mIconNormalExitDistance;
+ private int mIconEarlyExitDistance;
+ private final TransactionPool mTransactionPool;
+
+ SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
+ int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
mContext = context;
- mMaxIconAnimationDuration = maxIconAnimationDuration;
+ mMaxAnimatableIconDuration = maxAnimatableIconDuration;
+ mAppRevealDuration = appRevealAnimDuration;
+ mIconExitDuration = iconExitAnimDuration;
+ mTransactionPool = pool;
}
private void updateDensity() {
@@ -74,6 +86,12 @@ public class SplashscreenContentDrawer {
com.android.wm.shell.R.dimen.starting_surface_brand_image_width);
mBrandingImageHeight = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.starting_surface_brand_image_height);
+ mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
+ mIconNormalExitDistance = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.starting_surface_normal_exit_icon_distance);
+ mIconEarlyExitDistance = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.starting_surface_early_exit_icon_distance);
}
private int getSystemBGColor() {
@@ -119,7 +137,7 @@ public class SplashscreenContentDrawer {
if (attrs.mReplaceIcon != null) {
iconDrawable = attrs.mReplaceIcon;
animationDuration = Math.max(0,
- Math.min(attrs.mAnimationDuration, mMaxIconAnimationDuration));
+ Math.min(attrs.mAnimationDuration, mMaxAnimatableIconDuration));
} else {
iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
: context.getPackageManager().getDefaultActivityIcon();
@@ -439,8 +457,8 @@ public class SplashscreenContentDrawer {
}
/**
- * For ColorDrawable only.
- * There will be only one color so don't spend too much resource for it.
+ * For ColorDrawable only. There will be only one color so don't spend too much resource for
+ * it.
*/
private static class SingleColorTester implements ColorTester {
private final ColorDrawable mColorDrawable;
@@ -472,9 +490,8 @@ public class SplashscreenContentDrawer {
}
/**
- * For any other Drawable except ColorDrawable.
- * This will use the Palette API to check the color information and use a quantizer to
- * filter out transparent colors when needed.
+ * For any other Drawable except ColorDrawable. This will use the Palette API to check the
+ * color information and use a quantizer to filter out transparent colors when needed.
*/
private static class ComplexDrawableTester implements ColorTester {
private static final int MAX_BITMAP_SIZE = 40;
@@ -593,4 +610,17 @@ public class SplashscreenContentDrawer {
}
}
}
+
+ /**
+ * Create and play the default exit animation for splash screen view.
+ */
+ void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
+ Rect frame, boolean isEarlyExit, Runnable finishCallback) {
+ final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash,
+ frame, mAppRevealDuration, mIconExitDuration, mMainWindowShiftLength,
+ isEarlyExit ? mIconEarlyExitDistance : mIconNormalExitDistance, mTransactionPool,
+ finishCallback);
+ animation.prepareAnimations();
+ animation.startAnimations();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
index a594a9f31dde..f258286f2d17 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
@@ -16,7 +16,9 @@
package com.android.wm.shell.startingsurface;
+import android.graphics.Rect;
import android.os.IBinder;
+import android.view.SurfaceControl;
import android.window.StartingWindowInfo;
import java.util.function.BiConsumer;
@@ -31,7 +33,8 @@ public interface StartingSurface {
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
- void removeStartingWindow(int taskId);
+ void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation);
/**
* Called when the Task wants to copy the splash screen.
* @param taskId
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 2d1d65b87718..9212c4b86105 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -29,14 +29,19 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.Choreographer;
import android.view.Display;
+import android.view.SurfaceControl;
import android.view.View;
-import android.view.Window;
import android.view.WindowManager;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
@@ -46,6 +51,7 @@ import android.window.TaskSnapshot;
import com.android.internal.R;
import com.android.internal.policy.PhoneWindow;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
import java.util.function.Consumer;
@@ -62,23 +68,20 @@ public class StartingSurfaceDrawer {
private final DisplayManager mDisplayManager;
private final ShellExecutor mSplashScreenExecutor;
private final SplashscreenContentDrawer mSplashscreenContentDrawer;
- protected Choreographer mChoreographer;
- // TODO(b/131727939) remove this when clearing ActivityRecord
- private static final int REMOVE_WHEN_TIMEOUT = 2000;
-
- public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor) {
+ public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
+ TransactionPool pool) {
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mSplashScreenExecutor = splashScreenExecutor;
- final int maxIconAnimDuration = context.getResources().getInteger(
+ final int maxAnimatableIconDuration = context.getResources().getInteger(
com.android.wm.shell.R.integer.max_starting_window_intro_icon_anim_duration);
- mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, maxIconAnimDuration);
- mSplashScreenExecutor.execute(this::initChoreographer);
- }
-
- protected void initChoreographer() {
- mChoreographer = Choreographer.getInstance();
+ final int iconExitAnimDuration = context.getResources().getInteger(
+ com.android.wm.shell.R.integer.starting_window_icon_exit_anim_duration);
+ final int appRevealAnimDuration = context.getResources().getInteger(
+ com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext,
+ maxAnimatableIconDuration, iconExitAnimDuration, appRevealAnimDuration, pool);
}
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -144,11 +147,12 @@ public class StartingSurfaceDrawer {
context = displayContext;
if (theme != context.getThemeResId() || labelRes != 0) {
try {
- context = context.createPackageContext(
- activityInfo.packageName, CONTEXT_RESTRICTED);
+ context = context.createPackageContextAsUser(activityInfo.packageName,
+ CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
context.setTheme(theme);
} catch (PackageManager.NameNotFoundException e) {
- // Ignore
+ Slog.w(TAG, "Failed creating package context with package name "
+ + activityInfo.packageName + " for user " + taskInfo.userId, e);
}
}
@@ -195,6 +199,7 @@ public class StartingSurfaceDrawer {
}
final PhoneWindow win = new PhoneWindow(context);
+ win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
win.setIsStartingWindow(true);
CharSequence label = context.getResources().getText(labelRes, null);
@@ -211,7 +216,7 @@ public class StartingSurfaceDrawer {
// the keyguard is being hidden. This is okay because starting windows never show
// secret information.
// TODO(b/113840485): Occluded may not only happen on default display
- if (displayId == DEFAULT_DISPLAY) {
+ if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
}
@@ -247,6 +252,7 @@ public class StartingSurfaceDrawer {
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+ params.format = PixelFormat.RGBA_8888;
final Resources res = context.getResources();
final boolean supportsScreen = res != null && (res.getCompatibilityInfo() != null
@@ -257,98 +263,25 @@ public class StartingSurfaceDrawer {
params.setTitle("Splash Screen " + activityInfo.packageName);
- // TODO(b/173975965) If the target activity doesn't request FLAG_HARDWARE_ACCELERATED, we
- // cannot replace the content view after first view was drawn, sounds like an issue.
- new AddSplashScreenViewRunnable(taskInfo.taskId, win, context, appToken, params, iconRes,
- splashscreenContentResId[0], enableHardAccelerated).run();
- }
-
- private class AddSplashScreenViewRunnable implements Runnable {
- private final int mTaskId;
- private final Window mWin;
- private final IBinder mAppToken;
- private final WindowManager.LayoutParams mLayoutParams;
- private final Context mContext;
- private final int mIconRes;
- private final int mSplashscreenContentResId;
- private final boolean mReplaceSplashScreenView;
- private int mSequence;
-
- AddSplashScreenViewRunnable(int taskId, Window window, Context context,
- IBinder appToken, WindowManager.LayoutParams params, int iconRes,
- int splashscreenContentResId, boolean replaceSplashScreenView) {
- mTaskId = taskId;
- mWin = window;
- mAppToken = appToken;
- mContext = context;
- mLayoutParams = params;
- mIconRes = iconRes;
- mSplashscreenContentResId = splashscreenContentResId;
- mReplaceSplashScreenView = replaceSplashScreenView;
- }
-
- private void createInitialView() {
- View tempView = new View(mContext);
- mWin.setContentView(tempView);
- mSequence++;
- final View view = mWin.getDecorView();
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) {
- mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, this, null);
- }
- }
-
- private SplashScreenView replaceRealView() {
- final SplashScreenView sView =
- mSplashscreenContentDrawer.makeSplashScreenContentView(mContext,
- mIconRes, mSplashscreenContentResId);
- mWin.setContentView(sView);
- sView.cacheRootWindow(mWin);
- return sView;
- }
-
- private SplashScreenView initiateOnce() {
- final SplashScreenView sView =
- mSplashscreenContentDrawer.makeSplashScreenContentView(mContext, mIconRes,
- mSplashscreenContentResId);
- final View view = mWin.getDecorView();
+ // TODO(b/173975965) tracking performance
+ final int taskId = taskInfo.taskId;
+ SplashScreenView sView = null;
+ try {
+ sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes,
+ splashscreenContentResId[0]);
+ final View view = win.getDecorView();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
- if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) {
- mWin.setContentView(sView);
- sView.cacheRootWindow(mWin);
- }
- return sView;
- }
-
- @Override
- public void run() {
- SplashScreenView view = null;
- boolean setRecord = false;
- try {
- if (mReplaceSplashScreenView) {
- // Tricky way to make animation start faster... create the real content after
- // first window drawn. The first empty window won't been see because wm will
- // still need to wait for transition ready.
- if (mSequence == 0) {
- createInitialView();
- } else if (mSequence == 1) {
- setRecord = true;
- view = replaceRealView();
- }
- } else {
- setRecord = true;
- view = initiateOnce();
- }
- } catch (RuntimeException e) {
- // don't crash if something else bad happens, for example a
- // failure loading resources because we are loading from an app
- // on external storage that has been unmounted.
- Slog.w(TAG, " failed creating starting window", e);
- } finally {
- if (setRecord) {
- setSplashScreenRecord(mTaskId, view);
- }
+ if (postAddWindow(taskId, appToken, view, wm, params)) {
+ win.setContentView(sView);
+ sView.cacheRootWindow(win);
}
+ } catch (RuntimeException e) {
+ // don't crash if something else bad happens, for example a
+ // failure loading resources because we are loading from an app
+ // on external storage that has been unmounted.
+ Slog.w(TAG, " failed creating starting window", e);
+ } finally {
+ setSplashScreenRecord(taskId, sView);
}
}
@@ -359,21 +292,20 @@ public class StartingSurfaceDrawer {
TaskSnapshot snapshot) {
final int taskId = startingWindowInfo.taskInfo.taskId;
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
- snapshot, mSplashScreenExecutor, () -> removeWindowSynced(taskId));
- mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
- final StartingWindowRecord tView =
- new StartingWindowRecord(null/* decorView */, surface);
+ snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId));
+ final StartingWindowRecord tView = new StartingWindowRecord(null/* decorView */, surface);
mStartingWindowRecords.put(taskId, tView);
}
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
- public void removeStartingWindow(int taskId) {
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
}
- removeWindowSynced(taskId);
+ removeWindowSynced(taskId, leash, frame, playRevealAnimation);
}
/**
@@ -383,13 +315,6 @@ public class StartingSurfaceDrawer {
public void copySplashScreenView(int taskId) {
final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
SplashScreenViewParcelable parcelable;
- if (preView != null) {
- if (preView.isWaitForContent()) {
- mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
- () -> copySplashScreenView(taskId), null);
- return;
- }
- }
if (preView != null && preView.mContentView != null
&& preView.mContentView.isCopyable()) {
parcelable = new SplashScreenViewParcelable(preView.mContentView);
@@ -413,12 +338,6 @@ public class StartingSurfaceDrawer {
Slog.w(TAG, appToken + " already running, starting window not displayed. "
+ e.getMessage());
shouldSaveView = false;
- } catch (RuntimeException e) {
- // don't crash if something else bad happens, for example a
- // failure loading resources because we are loading from an app
- // on external storage that has been unmounted.
- Slog.w(TAG, appToken + " failed creating starting window", e);
- shouldSaveView = false;
} finally {
if (view != null && view.getParent() == null) {
Slog.w(TAG, "view not successfully added to wm, removing view");
@@ -427,9 +346,7 @@ public class StartingSurfaceDrawer {
}
}
if (shouldSaveView) {
- removeWindowSynced(taskId);
- mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId),
- REMOVE_WHEN_TIMEOUT);
+ removeWindowNoAnimate(taskId);
saveSplashScreenRecord(taskId, view);
}
return shouldSaveView;
@@ -449,24 +366,29 @@ public class StartingSurfaceDrawer {
}
}
- protected void removeWindowSynced(int taskId) {
+ private void removeWindowNoAnimate(int taskId) {
+ removeWindowSynced(taskId, null, null, false);
+ }
+
+ protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
- if (record.isWaitForContent()) {
- if (DEBUG_SPLASH_SCREEN) {
- Slog.v(TAG, "splash screen window haven't been draw yet");
- }
- mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
- () -> removeWindowSynced(taskId), null);
- return;
- }
if (record.mDecorView != null) {
if (DEBUG_SPLASH_SCREEN) {
Slog.v(TAG, "Removing splash screen window for task: " + taskId);
}
- final WindowManager wm = record.mDecorView.getContext()
- .getSystemService(WindowManager.class);
- wm.removeView(record.mDecorView);
+ if (record.mContentView != null) {
+ final HandleExitFinish exitFinish = new HandleExitFinish(record.mDecorView);
+ if (leash != null || playRevealAnimation) {
+ mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
+ leash, frame, record.isEarlyExit(), exitFinish);
+ } else {
+ // the SplashScreenView has been copied to client, skip default exit
+ // animation
+ exitFinish.run();
+ }
+ }
}
if (record.mTaskSnapshotWindow != null) {
if (DEBUG_TASK_SNAPSHOT) {
@@ -478,6 +400,26 @@ public class StartingSurfaceDrawer {
}
}
+ private static class HandleExitFinish implements Runnable {
+ private View mDecorView;
+
+ HandleExitFinish(View decorView) {
+ mDecorView = decorView;
+ }
+
+ @Override
+ public void run() {
+ if (mDecorView == null) {
+ return;
+ }
+ final WindowManager wm = mDecorView.getContext().getSystemService(WindowManager.class);
+ if (wm != null) {
+ wm.removeView(mDecorView);
+ }
+ mDecorView = null;
+ }
+ }
+
private void getWindowResFromContext(Context ctx, Consumer<TypedArray> consumer) {
final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
consumer.accept(a);
@@ -488,10 +430,12 @@ public class StartingSurfaceDrawer {
* Record the view or surface for a starting window.
*/
private static class StartingWindowRecord {
+ private static final long EARLY_START_MINIMUM_TIME_MS = 250;
private final View mDecorView;
private final TaskSnapshotWindow mTaskSnapshotWindow;
private SplashScreenView mContentView;
private boolean mSetSplashScreen;
+ private long mContentCreateTime;
StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
mDecorView = decorView;
@@ -503,11 +447,12 @@ public class StartingSurfaceDrawer {
return;
}
mContentView = splashScreenView;
+ mContentCreateTime = SystemClock.uptimeMillis();
mSetSplashScreen = true;
}
- private boolean isWaitForContent() {
- return mDecorView != null && !mSetSplashScreen;
+ boolean isEarlyExit() {
+ return SystemClock.uptimeMillis() - mContentCreateTime < EARLY_START_MINIMUM_TIME_MS;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index 5eb7071fbd63..a694e525a761 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -28,14 +28,17 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.SurfaceControl;
import android.window.StartingWindowInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
import java.util.function.BiConsumer;
@@ -67,8 +70,16 @@ public class StartingWindowController {
private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
private final ShellExecutor mSplashScreenExecutor;
+ // For Car Launcher
public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) {
- mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor);
+ mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor,
+ new TransactionPool());
+ mSplashScreenExecutor = splashScreenExecutor;
+ }
+
+ public StartingWindowController(Context context, ShellExecutor splashScreenExecutor,
+ TransactionPool pool) {
+ mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool);
mSplashScreenExecutor = splashScreenExecutor;
}
@@ -112,7 +123,8 @@ public class StartingWindowController {
+ " allowTaskSnapshot " + allowTaskSnapshot
+ " activityCreated " + activityCreated);
}
- if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+ if ((newTask || !processRunning || (taskSwitch && !activityCreated))
+ && windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
}
if (taskSwitch && allowTaskSnapshot) {
@@ -198,8 +210,9 @@ public class StartingWindowController {
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
- void removeStartingWindow(int taskId) {
- mStartingSurfaceDrawer.removeStartingWindow(taskId);
+ void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
+ mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
}
private class StartingSurfaceImpl implements StartingSurface {
@@ -211,9 +224,11 @@ public class StartingWindowController {
}
@Override
- public void removeStartingWindow(int taskId) {
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
mSplashScreenExecutor.execute(() ->
- StartingWindowController.this.removeStartingWindow(taskId));
+ StartingWindowController.this.removeStartingWindow(taskId, leash, frame,
+ playRevealAnimation));
}
@Override
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index f06d57c6c789..ad4ccc0288ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -11,6 +11,8 @@
<option name="force-skip-system-props" value="true" />
<!-- set WM tracing verbose level to all -->
<option name="run-command" value="cmd window tracing level all" />
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame" />
<!-- restart launcher to activate TAPL -->
<option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
</target_preparer>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 90e71373b1fd..98ce2747d532 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,6 +18,8 @@ package com.android.wm.shell.flicker.apppairs
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -25,6 +27,8 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,11 +36,10 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test AppPairs launch.
- * To run this test: `atest WMShellFlickerTests:AppPairsTest`
- */
-/**
- * Test cold launch app from launcher.
+ * Test cold launch app from launcher. When the device doesn't support non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair
+ * non-resizable apps.
+ *
* To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
*/
@RequiresDevice
@@ -46,6 +49,7 @@ import org.junit.runners.Parameterized
class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -59,6 +63,32 @@ class AppPairsTestCannotPairNonResizeableApps(
}
}
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 1) {
+ // Not support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index dc51b4fb5a9e..63e9a787aa17 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -56,6 +56,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
new file mode 100644
index 000000000000..1e3595c17f48
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launch app from launcher. When the device supports non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair
+ * non-resizable apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTestSupportPairNonResizeableApps(
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ nonResizeableApp?.launchViaIntent(wmHelper)
+ // TODO pair apps through normal UX flow
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 0) {
+ // Support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+ @Presubmit
+ @Test
+ fun bothAppWindowVisible() {
+ val nonResizeableApp = nonResizeableApp
+ require(nonResizeableApp != null) {
+ "Non resizeable app not initialized"
+ }
+ testSpec.assertWmEnd {
+ isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(primaryApp.defaultWindowName)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = AppPairsHelper.TEST_REPETITIONS)
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 5bb9b2f8b8ca..234dda448cc8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -61,6 +61,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 91e080f65550..134d00be73e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -17,22 +17,27 @@
package com.android.wm.shell.flicker.apppairs
import android.app.Instrumentation
+import android.content.Context
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
import android.util.Log
+import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -42,6 +47,7 @@ import java.io.IOException
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val context: Context = instrumentation.context
protected val isRotated = testSpec.config.startRotation.isRotated()
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
@@ -134,17 +140,39 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarLayerRotatesAndScales() {
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+ }
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+ }
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 5f003ba62b2d..d341bb1e6aa9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -27,8 +27,6 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
@@ -75,16 +73,6 @@ class RotateTwoLaunchedAppsInAppPairsMode(
@Test
fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
@FlakyTest(bugId = 172776659)
@Test
fun appPairsPrimaryBoundsIsVisible() =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index d4792088ac31..3bf0296fee20 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -27,16 +27,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,20 +64,10 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
- @Presubmit
- @Test
fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
- Surface.ROTATION_0, testSpec.config.endRotation)
-
- @Presubmit
- @Test
override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index ecc066be734f..5a96a7c8cbd9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Point
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.os.SystemClock
@@ -135,11 +134,8 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
expandPipWindow(wmHelper)
val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
requireNotNull(exitPipObject) { "PIP window dismiss button not found" }
- val coordinatesInWindow = exitPipObject.visibleBounds
- val windowOffset = wmHelper.getWindowRegion(component).bounds
- val newCoordinates = Point(windowOffset.left + coordinatesInWindow.centerX(),
- windowOffset.top + coordinatesInWindow.centerY())
- uiDevice.click(newCoordinates.x, newCoordinates.y)
+ val dismissButtonBounds = exitPipObject.visibleBounds
+ uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
// Wait for animation to complete.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 17c51fb15b0c..bca257646e11 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -29,8 +29,7 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -60,6 +59,11 @@ class EnterSplitScreenDockActivity(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
+ splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 169271943)
@Test
fun dockedStackPrimaryBoundsIsVisible() =
@@ -73,12 +77,8 @@ class EnterSplitScreenDockActivity(
@FlakyTest(bugId = 178531736)
@Test
// b/178531736
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -91,12 +91,8 @@ class EnterSplitScreenDockActivity(
@FlakyTest(bugId = 178531736)
@Test
// b/178531736
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index a94fd463c624..9000f22fb03d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
@@ -62,6 +61,11 @@ class EnterSplitScreenLaunchToSide(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 169271943)
@Test
fun dockedStackPrimaryBoundsIsVisible() =
@@ -83,12 +87,8 @@ class EnterSplitScreenLaunchToSide(
@Test
// TODO(b/178447631) Remove Splash Screen from white list when flicker lib
// add a wait for splash screen be gone
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -104,12 +104,8 @@ class EnterSplitScreenLaunchToSide(
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
index 238059b484b5..219da27c653d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
@@ -22,12 +22,10 @@ import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.WALLPAPER_TITLE
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.flicker.helpers.openQuickstep
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.Assert
@@ -66,28 +64,24 @@ class EnterSplitScreenNonResizableNotDock(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+
@Test
fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- SPLASH_SCREEN_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(WALLPAPER_TITLE,
- LAUNCHER_PACKAGE_NAME,
- SPLASH_SCREEN_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Test
fun appWindowIsVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index acd570a3773e..9717709852d4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,17 +69,20 @@ class ExitLegacySplitScreenFromBottom(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER)
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
@Presubmit
@Test
@@ -97,11 +99,8 @@ class ExitLegacySplitScreenFromBottom(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index cef188695ce7..3f714bb6b6c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,6 +69,11 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 175687842)
@Test
fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
@@ -80,11 +84,8 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -101,11 +102,8 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
index 1e89a25c06df..08d5db0f9124 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
@@ -17,11 +17,13 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.view.Surface
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import org.junit.Test
abstract class LegacySplitScreenRotateTransition(
testSpec: FlickerTestParameter
@@ -44,4 +46,16 @@ abstract class LegacySplitScreenRotateTransition(
}
}
}
+
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 7f69a66e6e82..72d6f569ab0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -34,14 +34,13 @@ import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -89,6 +88,10 @@ class LegacySplitScreenToLauncher(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
@@ -99,11 +102,6 @@ class LegacySplitScreenToLauncher(
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
@Presubmit
@@ -122,8 +120,8 @@ class LegacySplitScreenToLauncher(
@Presubmit
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName))
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 91ea8716e4f0..319fde14e418 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
@@ -29,7 +30,9 @@ import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Test
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -40,6 +43,15 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage
+ /**
+ * List of windows that are ignored when verifying that visible elements appear on 2
+ * consecutive entries in the trace.
+ *
+ * b/182720234
+ */
+ open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
setup {
@@ -88,11 +100,26 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
}
}
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoredWindows)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoredWindows)
+ }
+ }
+
companion object {
internal const val LIVE_WALLPAPER_PACKAGE_NAME =
"com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
internal const val LETTERBOX_NAME = "Letterbox"
internal const val TOAST_NAME = "Toast"
- internal const val SPLASH_SCREEN_NAME = "Splash Screen"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
index caafa278d297..8a1715ee2585 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -30,8 +30,7 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -67,19 +66,20 @@ class NonResizableDismissInLegacySplitScreen(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
+ splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -97,13 +97,8 @@ class NonResizableDismissInLegacySplitScreen(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
index 543484ac9759..4c6705f6e739 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -29,8 +29,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -67,6 +66,12 @@ class NonResizableLaunchInLegacySplitScreen(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
+ nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
@@ -77,14 +82,8 @@ class NonResizableLaunchInLegacySplitScreen(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER,
- LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -98,14 +97,8 @@ class NonResizableLaunchInLegacySplitScreen(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER,
- LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index d22833784bcf..8f15e5088914 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -31,8 +31,7 @@ import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -61,12 +60,15 @@ class OpenAppToLegacySplitScreen(
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -90,10 +92,8 @@ class OpenAppToLegacySplitScreen(
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@FlakyTest(bugId = 151179149)
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index f5174bc3cef7..f40a08ca341c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -40,8 +40,6 @@ import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
@@ -107,8 +105,11 @@ class ResizeLegacySplitScreen(
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
@FlakyTest(bugId = 156223549)
@Test
@@ -144,8 +145,8 @@ class ResizeLegacySplitScreen(
testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Test
fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 75c33c671008..033322786d8f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -23,13 +23,7 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,7 +42,6 @@ class EnterExitPipTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
private val testApp = FixedAppHelper(instrumentation)
- private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) {
@@ -84,14 +77,6 @@ class EnterExitPipTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun showBothAppLayersThenHidePip() {
testSpec.assertLayers {
isVisible(testApp.defaultWindowName)
@@ -118,14 +103,6 @@ class EnterExitPipTest(
}
}
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 2f08db1b7d0a..4847c98f54e9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -24,14 +25,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.startRotation
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -57,15 +50,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun pipWindowBecomesVisible() {
+ fun pipAppWindowAlwaysVisible() {
testSpec.assertWm {
this.showsAppWindow(pipApp.defaultWindowName)
}
@@ -73,34 +58,37 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
fun pipLayerBecomesVisible() {
testSpec.assertLayers {
this.isVisible(pipApp.launcherName)
}
}
+ @Postsubmit
+ @Test
+ fun pipWindowBecomesVisible() {
+ testSpec.assertWm {
+ invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
+ .then()
+ .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+
@FlakyTest(bugId = 140855415)
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@FlakyTest(bugId = 140855415)
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@FlakyTest(bugId = 140855415)
@Test
- fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun noUncoveredRegions() = super.noUncoveredRegions()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 9011f1a9fb6a..ba88ee5751de 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -18,16 +18,13 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -83,6 +80,14 @@ class EnterPipToOtherOrientationTest(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun pipAppWindowIsAlwaysOnTop() {
@@ -109,14 +114,6 @@ class EnterPipToOtherOrientationTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun pipAppLayerHidesTestApp() {
testSpec.assertLayersStart {
coversExactly(startingBounds, pipApp.defaultWindowName)
@@ -132,14 +129,6 @@ class EnterPipToOtherOrientationTest(
}
}
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index 3e331761f767..eae7e973711c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -24,14 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.Test
import org.junit.runners.Parameterized
@@ -52,22 +45,6 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
@Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -86,21 +63,6 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
}
}
- @Presubmit
- @Test
- open fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
- open fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
- open fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
@FlakyTest(bugId = 151179149)
@Test
open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
index afaf33a7c46f..c7a1c9aac86b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -51,40 +51,40 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition
}
}
- @Postsubmit
+ @Presubmit
@Test
override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
- @Postsubmit
+ @Presubmit
@Test
override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- @Postsubmit
+ @Presubmit
@Test
override fun noUncoveredRegions() = super.noUncoveredRegions()
- @Postsubmit
+ @Presubmit
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 97afc65b8b61..bf148bc83d46 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -24,17 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
@@ -55,7 +49,6 @@ import org.junit.runners.Parameterized
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
- private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -85,7 +78,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Postsubmit
+ @Presubmit
@Test
fun pipWindowInsideDisplayBounds() {
testSpec.assertWm {
@@ -93,7 +86,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Postsubmit
+ @Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
@@ -103,15 +96,15 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Postsubmit
+ @Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerInsideDisplayBounds() {
testSpec.assertLayers {
@@ -119,7 +112,7 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Postsubmit
+ @Presubmit
@Test
fun bothAppLayersVisible() {
testSpec.assertLayersEnd {
@@ -128,13 +121,13 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Postsubmit
+ @Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
companion object {
const val TEST_REPETITIONS = 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index 4c95da284d9e..d011419150e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -24,8 +24,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
@@ -59,11 +57,11 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Postsubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
@Postsubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
@Postsubmit
@Test
@@ -71,6 +69,14 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Postsubmit
@Test
+ fun pipLayerInsideDisplay() {
+ testSpec.assertLayersStart {
+ coversAtMost(displayBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ @Postsubmit
+ @Test
fun pipWindowMovesUp() = testSpec.assertWmEnd {
val initialState = this.trace?.first()?.wmState
?: error("Trace should not be empty")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index df835d21e73f..49a1055af4a0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -29,10 +29,6 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
@@ -77,34 +73,18 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
@FlakyTest(bugId = 140855415)
@Test
- fun navBarLayerRotatesAndScales() =
+ override fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
@FlakyTest(bugId = 140855415)
@Test
- fun statusBarLayerRotatesScales() =
+ override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
testSpec.config.endRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 1bb1d2861f3f..945a20b28ff0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -26,14 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -68,22 +61,6 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun appReplacesPipWindow() {
testSpec.assertWm {
this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -94,11 +71,6 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
fun appReplacesPipLayer() {
testSpec.assertLayers {
this.isVisible(PIP_WINDOW_TITLE)
@@ -107,15 +79,13 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
}
}
- @Presubmit
- @Test
- fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
+ @FlakyTest
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ fun testAppCoversFullScreen() {
+ testSpec.assertLayersStart {
+ coversExactly(displayBounds, pipApp.defaultWindowName)
+ }
+ }
@FlakyTest(bugId = 151179149)
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b0a9afef9215..7dc7e7d25b79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -18,27 +18,37 @@ package com.android.wm.shell.flicker.pip
import android.app.Instrumentation
import android.content.Intent
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Test
abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val isRotated = testSpec.config.startRotation.isRotated()
protected val pipApp = PipAppHelper(instrumentation)
+ protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-
// Helper class to process test actions by broadcast.
protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -148,4 +158,35 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
extraSpec(this, configuration)
}
}
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ open fun noUncoveredRegions() =
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 7916ce59af52..67e1768f3a9f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -25,10 +26,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
@@ -83,6 +80,14 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun pipWindowInsideDisplay() {
@@ -101,20 +106,18 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
coversAtMost(startingBounds, pipApp.defaultWindowName)
}
}
+ @Postsubmit
+ @Test
+ fun pipAlwaysVisible() = testSpec.assertWm {
+ this.showsAppWindow(pipApp.windowName)
+ }
+
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
@@ -123,14 +126,6 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index c5221dee9216..bd5fe2bcbdad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -16,11 +16,10 @@
package com.android.wm.shell.onehanded;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
@@ -67,6 +66,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedGestureHandler mMockGestureHandler;
@Mock
+ OneHandedSettingsUtil mMockSettingsUitl;
+ @Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
IOverlayManager mMockOverlayManager;
@@ -79,13 +80,9 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
Handler mMockShellMainHandler;
- final boolean mDefaultEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- getTestContext().getContentResolver());
- final boolean mDefaultSwipeToNotificationEnabled =
- OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- getTestContext().getContentResolver());
- final boolean mDefaultTapAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
- getTestContext().getContentResolver());
+ final boolean mDefaultEnabled = true;
+ final boolean mDefaultSwipeToNotificationEnabled = false;
+ final boolean mDefaultTapAppToExitEnabled = true;
@Before
public void setUp() {
@@ -97,6 +94,14 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
+ when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any())).thenReturn(
+ mDefaultEnabled);
+ when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any())).thenReturn(
+ OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
+ when(mMockSettingsUitl.getSettingsTapsAppToExit(any())).thenReturn(
+ mDefaultTapAppToExitEnabled);
+ when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any())).thenReturn(
+ mDefaultSwipeToNotificationEnabled);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
@@ -107,6 +112,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mMockTouchHandler,
mMockTutorialHandler,
mMockGestureHandler,
+ mMockSettingsUitl,
mSpiedTimeoutHandler,
mMockUiEventLogger,
mMockOverlayManager,
@@ -121,22 +127,13 @@ public class OneHandedControllerTest extends OneHandedTestCase {
final OneHandedAnimationController animationController = new OneHandedAnimationController(
mContext);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mWindowManager, mMockDisplayController, animationController,
- mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor);
+ mContext, mWindowManager, animationController, mMockTutorialHandler,
+ mMockBackgroundOrganizer, mMockShellMainExecutor);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
@Test
- public void testNoRegisterAndUnregisterInSameCall() {
- if (mDefaultEnabled) {
- verify(mMockDisplayAreaOrganizer, never()).unregisterOrganizer();
- } else {
- verify(mMockDisplayAreaOrganizer, never()).registerOrganizer(FEATURE_ONE_HANDED);
- }
- }
-
- @Test
public void testStartOneHandedShouldTriggerScheduleOffset() {
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
mSpiedOneHandedController.setOneHandedEnabled(true);
@@ -190,35 +187,39 @@ public class OneHandedControllerTest extends OneHandedTestCase {
public void testUpdateEnabled() {
mSpiedOneHandedController.setOneHandedEnabled(true);
- verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled);
- verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(
- mDefaultEnabled || mDefaultSwipeToNotificationEnabled);
+ verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
+ verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
}
@Test
public void testUpdateSwipeToNotification() {
mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled);
- verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled);
- verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(
- mDefaultEnabled || mDefaultSwipeToNotificationEnabled);
+ verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
+ verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
}
@Test
- public void testSettingsObserverUpdateTapAppToExit() {
- mSpiedOneHandedController.onTaskChangeExitSettingChanged();
- if (mDefaultTapAppToExitEnabled) {
- verify(mMockTaskStackListener, atLeastOnce()).addListener(any());
- } else {
- verify(mMockTaskStackListener, atLeastOnce()).removeListener(any());
- }
+ public void testTapAppToExitEnabledAddListener() {
+ mSpiedOneHandedController.setTaskChangeToExit(mDefaultTapAppToExitEnabled);
+
+ // If device settings default ON, then addListener() will be trigger 1 time at init
+ verify(mMockTaskStackListener, atLeastOnce()).addListener(any());
+ }
+
+ @Test
+ public void testTapAppToExitDisabledRemoveListener() {
+ mSpiedOneHandedController.setTaskChangeToExit(!mDefaultTapAppToExitEnabled);
+
+ // If device settings default ON, then removeListener() will be trigger 1 time at init
+ verify(mMockTaskStackListener, atLeastOnce()).removeListener(any());
}
@Test
public void testSettingsObserverUpdateEnabled() {
mSpiedOneHandedController.onEnabledSettingChanged();
- verify(mSpiedOneHandedController).setOneHandedEnabled(mDefaultEnabled);
+ verify(mSpiedOneHandedController).setOneHandedEnabled(anyBoolean());
}
@Test
@@ -232,14 +233,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
public void testSettingsObserverUpdateSwipeToNotification() {
mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged();
- // Swipe to notification function is opposite with one handed mode function
- if (mDefaultSwipeToNotificationEnabled) {
- verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(
- mDefaultSwipeToNotificationEnabled);
- } else {
- verify(mSpiedOneHandedController, never()).setSwipeToNotificationEnabled(
- mDefaultSwipeToNotificationEnabled);
- }
+ verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 1fa1e2ff69b6..f897b09f8b8b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -122,7 +122,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
mWindowManager,
- mMockDisplayController,
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
@@ -170,6 +169,15 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
}
@Test
+ public void testRotation_getNewDisplayBounds() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 0 -> 90
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
+ mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).getDisplayBounds();
+ }
+
+ @Test
public void testRotation_portrait_0_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 90
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
index 61643d86c8d9..1e6c41af4397 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
@@ -16,17 +16,11 @@
package com.android.wm.shell.onehanded;
-import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
-import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
-import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
-import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
-
-import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
import android.content.ContentResolver;
import android.database.ContentObserver;
-import android.net.Uri;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -34,76 +28,30 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedSettingsUtilTest extends OneHandedTestCase {
- ContentResolver mContentResolver;
- ContentObserver mContentObserver;
- boolean mOnChanged;
+ OneHandedSettingsUtil mSettingsUtil;
+
+ @Mock
+ ContentResolver mMockContentResolver;
+ @Mock
+ ContentObserver mMockContentObserver;
@Before
public void setUp() {
- mContentResolver = mContext.getContentResolver();
- mContentObserver = new ContentObserver(mContext.getMainThreadHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- mOnChanged = true;
- }
- };
- }
-
- @Test
- public void testRegisterSecureKeyObserver() {
- final Uri result = OneHandedSettingsUtil.registerSettingsKeyObserver(
- Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
+ MockitoAnnotations.initMocks(this);
- assertThat(result).isNotNull();
-
- OneHandedSettingsUtil.registerSettingsKeyObserver(
- Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
+ mSettingsUtil = new OneHandedSettingsUtil();
}
@Test
public void testUnregisterSecureKeyObserver() {
- OneHandedSettingsUtil.registerSettingsKeyObserver(
- Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
- OneHandedSettingsUtil.unregisterSettingsKeyObserver(mContentResolver, mContentObserver);
-
- assertThat(mOnChanged).isFalse();
-
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.TAPS_APP_TO_EXIT, 0);
-
- assertThat(mOnChanged).isFalse();
- }
+ mSettingsUtil.unregisterSettingsKeyObserver(mMockContentResolver, mMockContentObserver);
- @Test
- public void testGetSettingsIsOneHandedModeEnabled() {
- assertThat(OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContentResolver)).isAnyOf(true, false);
- }
-
- @Test
- public void testGetSettingsTapsAppToExit() {
- assertThat(OneHandedSettingsUtil.getSettingsTapsAppToExit(
- mContentResolver)).isAnyOf(true, false);
- }
-
- @Test
- public void testGetSettingsOneHandedModeTimeout() {
- assertThat(OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
- mContentResolver)).isAnyOf(
- ONE_HANDED_TIMEOUT_NEVER,
- ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS,
- ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS,
- ONE_HANDED_TIMEOUT_LONG_IN_SECONDS);
- }
-
- @Test
- public void testGetSettingsSwipeToNotificationEnabled() {
- assertThat(OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContentResolver)).isAnyOf(true, false);
+ verify(mMockContentResolver).unregisterContentObserver(any());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 69c537c2efbe..f586dda145d7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -64,6 +64,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
Handler mMockShellMainHandler;
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
+ @Mock
+ OneHandedSettingsUtil mMockSettingsUtil;
@Before
public void setUp() {
@@ -80,6 +82,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
mMockTouchHandler,
mMockTutorialHandler,
mMockGestureHandler,
+ mMockSettingsUtil,
mTimeoutHandler,
mMockUiEventLogger,
mMockOverlayManager,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index a531ef58725d..207db9e80511 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package unittest.src.com.android.wm.shell.startingsurface;
+package com.android.wm.shell.startingsurface;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -33,11 +33,12 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.testing.TestableContext;
-import android.view.Choreographer;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -49,7 +50,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
+import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
@@ -68,7 +69,7 @@ public class StartingSurfaceDrawerTests {
@Mock
private WindowManager mMockWindowManager;
@Mock
- private static Choreographer sFakeChoreographer;
+ private TransactionPool mTransactionPool;
TestStartingSurfaceDrawer mStartingSurfaceDrawer;
@@ -76,13 +77,9 @@ public class StartingSurfaceDrawerTests {
int mAddWindowForTask = 0;
int mViewThemeResId;
- TestStartingSurfaceDrawer(Context context, ShellExecutor executor) {
- super(context, executor);
- }
-
- @Override
- protected void initChoreographer() {
- mChoreographer = sFakeChoreographer;
+ TestStartingSurfaceDrawer(Context context, ShellExecutor animExecutor,
+ TransactionPool pool) {
+ super(context, animExecutor, pool);
}
@Override
@@ -95,7 +92,8 @@ public class StartingSurfaceDrawerTests {
}
@Override
- protected void removeWindowSynced(int taskId) {
+ protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
// listen for removeView
if (mAddWindowForTask == taskId) {
mAddWindowForTask = 0;
@@ -123,7 +121,8 @@ public class StartingSurfaceDrawerTests {
doNothing().when(mMockWindowManager).addView(any(), any());
mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context,
- new HandlerExecutor(new Handler(Looper.getMainLooper()))));
+ new HandlerExecutor(new Handler(Looper.getMainLooper())),
+ mTransactionPool));
}
@Test
@@ -137,9 +136,9 @@ public class StartingSurfaceDrawerTests {
verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
- mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId);
+ mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false);
waitHandlerIdle(mainLoop);
- verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId));
+ verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId), any(), any(), eq(false));
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 27e5f51d88b8..b908df20d3c0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package unittest.src.com.android.wm.shell.startingsurface;
+package com.android.wm.shell.startingsurface;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -48,7 +48,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index aba0f1b47673..63b831de5da1 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -97,8 +97,8 @@ cc_library {
"libincfs",
"libutils",
"libz",
- "libziparchive",
],
+ static_libs: ["libziparchive_for_incfs"],
static: {
enabled: false,
},
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index c0ef7be8b673..7e45f952d389 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -226,8 +226,6 @@ void AssetManager2::BuildDynamicRefTable() {
}
void AssetManager2::DumpToLog() const {
- base::ScopedLogSeverity _log(base::INFO);
-
LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
std::string list;
@@ -1721,7 +1719,6 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) {
}
void Theme::Dump() const {
- base::ScopedLogSeverity _log(base::INFO);
LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
for (int p = 0; p < packages_.size(); p++) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d663c52b2c08..607ef72df96a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -388,11 +388,10 @@ cc_defaults {
"liblog",
"libminikin",
"libz",
- "libziparchive",
"libjpeg",
],
- static_libs: ["libnativehelper_lazy"],
+ static_libs: ["libnativehelper_lazy", "libziparchive_for_incfs"],
target: {
android: {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 971a53a8b2dc..e58f31fd15eb 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -126,7 +126,7 @@ bool Properties::load() {
SkAndroidFrameworkTraceUtil::setEnableTracing(
base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
- runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
+ runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false);
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index dcb79babad24..42aa87b492e2 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -160,7 +160,7 @@ enum DebugLevel {
/**
* Property for whether this is running in the emulator.
*/
-#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
+#define PROPERTY_IS_EMULATOR "ro.boot.qemu"
///////////////////////////////////////////////////////////////////////////////
// Misc
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9793300b406d..800c58095041 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,6 +576,7 @@ void CanvasContext::draw() {
if (requireSwap) {
if (mExpectSurfaceStats) {
+ reportMetricsWithPresentTime();
std::lock_guard lock(mLast4FrameInfosMutex);
std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
next.first = mCurrentFrameInfo;
@@ -656,8 +657,6 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* cont
}
}
- instance->reportMetricsWithPresentTime();
-
if (frameInfo != nullptr) {
if (gpuCompleteTime == -1) {
gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e93824dfbd30..01126860b3ba 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -336,6 +336,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
GET_DEV_PROC(ResetCommandBuffer);
GET_DEV_PROC(ResetFences);
GET_DEV_PROC(WaitForFences);
+ GET_DEV_PROC(FrameBoundaryANDROID);
}
void VulkanManager::initialize() {
@@ -516,6 +517,25 @@ void VulkanManager::finishFrame(SkSurface* surface) {
if (semaphore != VK_NULL_HANDLE) {
if (submitted == GrSemaphoresSubmitted::kYes) {
mSwapSemaphore = semaphore;
+ if (mFrameBoundaryANDROID) {
+ // retrieve VkImage used as render target
+ VkImage image = VK_NULL_HANDLE;
+ GrBackendRenderTarget backendRenderTarget =
+ surface->getBackendRenderTarget(SkSurface::kFlushRead_BackendHandleAccess);
+ if (backendRenderTarget.isValid()) {
+ GrVkImageInfo info;
+ if (backendRenderTarget.getVkImageInfo(&info)) {
+ image = info.fImage;
+ } else {
+ ALOGE("Frame boundary: backend is not vulkan");
+ }
+ } else {
+ ALOGE("Frame boundary: invalid backend render target");
+ }
+ // frameBoundaryANDROID needs to know about mSwapSemaphore, but
+ // it won't wait on it.
+ mFrameBoundaryANDROID(mDevice, mSwapSemaphore, image);
+ }
} else {
destroy_semaphore(mDestroySemaphoreContext);
mDestroySemaphoreContext = nullptr;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 0912369b611d..7b5fe19c64f5 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -31,6 +31,21 @@
#include <vk/GrVkExtensions.h>
#include <vulkan/vulkan.h>
+// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
+// (https://github.com/google/agi) to enable profiling of apps rendering via
+// HWUI. This extension is not defined in Khronos, hence the need to declare it
+// manually here. There's a superseding extension (VK_EXT_frame_boundary) being
+// discussed in Khronos, but in the meantime we use the bespoke
+// VK_ANDROID_frame_boundary. This is a device extension that is implemented by
+// AGI's Vulkan capture layer, such that it is only supported by devices when
+// AGI is doing a capture of the app.
+//
+// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has
+// landed in the spec.
+typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore,
+ VkImage image);
+#define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary"
+
#include "Frame.h"
#include "IRenderPipeline.h"
#include "VulkanSurface.h"
@@ -160,6 +175,7 @@ private:
VkPtr<PFN_vkDestroyFence> mDestroyFence;
VkPtr<PFN_vkWaitForFences> mWaitForFences;
VkPtr<PFN_vkResetFences> mResetFences;
+ VkPtr<PFN_vkFrameBoundaryANDROID> mFrameBoundaryANDROID;
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 09c6a4fdf50d..a8f2d9a28d67 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -52,18 +52,10 @@ public:
return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
}
- static bool isBlendedShader(const SkShader* shader) {
- if (shader == nullptr) {
- return false;
- }
- return !shader->isOpaque();
- }
+ static bool isBlendedShader(const SkShader* shader) { return shader && !shader->isOpaque(); }
static bool isBlendedColorFilter(const SkColorFilter* filter) {
- if (filter == nullptr) {
- return false;
- }
- return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+ return filter && !filter->isAlphaUnchanged();
}
static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java
index 4b6e6882b50a..718977ff4296 100644
--- a/location/java/android/location/CorrelationVector.java
+++ b/location/java/android/location/CorrelationVector.java
@@ -77,6 +77,9 @@ public final class CorrelationVector implements Parcelable {
* be encoded as signed 16 bit integer where 1 is represented by 32767 and -1 is represented
* by -32768.
*
+ * <p>The values are quantized using a 16bit integer to save on the data size since the array
+ * contains real data and it might grow.
+ *
*/
@NonNull
public int[] getMagnitude() {
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 84a363d25c63..dbf26214066e 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -363,10 +363,10 @@ public final class GnssNavigationMessage implements Parcelable {
* <p>The bytes (or words) specified using big endian format (MSB first).
*
* <ul>
- * <li>For GPS L1 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10 30-bit words. Each
- * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with
- * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds,
- * respectively.</li>
+ * <li>For GPS L1 C/A, IRNSS L5 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10
+ * 30-bit words. Each word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip
+ * B31 and B32), with MSB first, for a total of 40 bytes, covering a time period of 6, 6, and
+ * 0.6 seconds, respectively.</li>
* <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum. These
* bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2
* seconds.</li>
diff --git a/media/Android.bp b/media/Android.bp
index 9268b22a929a..a66236e6f4ea 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -27,6 +27,9 @@ aidl_interface {
aidl_interface {
name: "media_permission-aidl",
unstable: true,
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
local_include_dir: "aidl",
srcs: [
"aidl/android/media/permission/Identity.aidl",
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
index 361497d59ea9..36389047cee8 100644
--- a/media/aidl/android/media/permission/Identity.aidl
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -22,11 +22,11 @@ package android.media.permission;
*/
parcelable Identity {
/** Linux user ID. */
- int uid;
+ int uid = -1;
/** Linux process ID. */
- int pid;
+ int pid = -1;
/** Package name. If null, the first package owned by the given uid will be assumed. */
- @nullable String packageName;
+ @nullable @utf8InCpp String packageName;
/** Attribution tag. Mostly used for diagnostic purposes. */
- @nullable String attributionTag;
+ @nullable @utf8InCpp String attributionTag;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1cef0922a48e..9e9d02e4eebf 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5287,7 +5287,10 @@ public class AudioManager {
* otherwise (typically one device, except for duplicated paths).
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
public @NonNull List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
@@ -5426,7 +5429,10 @@ public class AudioManager {
* @return the volume behavior for the device
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
public @DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify arguments (validity of device type is enforced in server)
@@ -5440,6 +5446,28 @@ public class AudioManager {
}
}
+ /**
+ * @hide
+ * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public boolean isFullVolumeDevice() {
+ final AudioAttributes attributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ final List<AudioDeviceAttributes> devices = getDevicesForAttributes(attributes);
+ for (AudioDeviceAttributes device : devices) {
+ if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Indicate wired accessory connection state change.
* @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
@@ -6729,7 +6757,7 @@ public class AudioManager {
*/
public Map<Integer, Boolean> getSurroundFormats() {
Map<Integer, Boolean> surroundFormats = new HashMap<>();
- int status = AudioSystem.getSurroundFormats(surroundFormats, false);
+ int status = AudioSystem.getSurroundFormats(surroundFormats);
if (status != AudioManager.SUCCESS) {
// fail and bail!
Log.e(TAG, "getSurroundFormats failed:" + status);
@@ -6762,20 +6790,17 @@ public class AudioManager {
/**
* @hide
* Returns all surround formats that are reported by the connected HDMI device.
- * The keys are not affected by calling setSurroundFormatEnabled(), and the values
- * are not affected by calling setSurroundFormatEnabled() when in AUTO mode.
- * This information can used to show the AUTO setting for SurroundSound.
+ * The return values are not affected by calling setSurroundFormatEnabled.
*
- * @return a map where the key is a surround format and
- * the value indicates the surround format is enabled or not
+ * @return a list of surround formats
*/
- public Map<Integer, Boolean> getReportedSurroundFormats() {
- Map<Integer, Boolean> reportedSurroundFormats = new HashMap<>();
- int status = AudioSystem.getSurroundFormats(reportedSurroundFormats, true);
+ public ArrayList<Integer> getReportedSurroundFormats() {
+ ArrayList<Integer> reportedSurroundFormats = new ArrayList<>();
+ int status = AudioSystem.getReportedSurroundFormats(reportedSurroundFormats);
if (status != AudioManager.SUCCESS) {
// fail and bail!
Log.e(TAG, "getReportedSurroundFormats failed:" + status);
- return new HashMap<Integer, Boolean>(); // Always return a map.
+ return new ArrayList<Integer>(); // Always return a list.
}
return reportedSurroundFormats;
}
@@ -7087,26 +7112,22 @@ public class AudioManager {
* <pre class="prettyprint">
* // Get an AudioManager instance
* AudioManager audioManager = Context.getSystemService(AudioManager.class);
- * try {
- * AudioDeviceInfo speakerDevice = null;
- * List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
- * for (AudioDeviceInfo device : devices) {
- * if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
- * speakerDevice = device;
- * break;
- * }
+ * AudioDeviceInfo speakerDevice = null;
+ * List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
+ * for (AudioDeviceInfo device : devices) {
+ * if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+ * speakerDevice = device;
+ * break;
* }
- * if (speakerDevice != null) {
- * // Turn speakerphone ON.
- * boolean result = audioManager.setCommunicationDevice(speakerDevice);
- * if (!result) {
- * // Handle error.
- * }
- * // Turn speakerphone OFF.
- * audioManager.clearCommunicationDevice();
+ * }
+ * if (speakerDevice != null) {
+ * // Turn speakerphone ON.
+ * boolean result = audioManager.setCommunicationDevice(speakerDevice);
+ * if (!result) {
+ * // Handle error.
* }
- * } catch (IllegalArgumentException e) {
- * // Handle exception.
+ * // Turn speakerphone OFF.
+ * audioManager.clearCommunicationDevice();
* }
* </pre>
* @param device the requested audio device.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index bf04b660425b..d7112d6dfa63 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,6 +16,8 @@
package android.media;
+import static android.media.permission.PermissionUtil.myIdentity;
+
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -26,9 +28,11 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.media.MediaRecorder.Source;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioPolicy;
+import android.media.permission.Identity;
import android.media.projection.MediaProjection;
import android.os.Binder;
import android.os.Build;
@@ -54,6 +58,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -352,6 +357,32 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
@RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int sessionId) throws IllegalArgumentException {
+ this(attributes, format, bufferSizeInBytes, sessionId, ActivityThread.currentApplication());
+ }
+
+ /**
+ * @hide
+ * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
+ * @param attributes a non-null {@link AudioAttributes} instance. Use
+ * {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio
+ * source for this instance.
+ * @param format a non-null {@link AudioFormat} instance describing the format of the data
+ * that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for
+ * configuring the audio format parameters such as encoding, channel mask and sample rate.
+ * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
+ * to during the recording. New audio data can be read from this buffer in smaller chunks
+ * than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
+ * required buffer size for the successful creation of an AudioRecord instance. Using values
+ * smaller than getMinBufferSize() will result in an initialization failure.
+ * @param sessionId ID of audio session the AudioRecord must be attached to, or
+ * {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
+ * time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before
+ * construction.
+ * @param context An optional context to pull an attribution tag from.
+ * @throws IllegalArgumentException
+ */
+ private AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
+ int sessionId, @Nullable Context context) throws IllegalArgumentException {
mRecordingState = RECORDSTATE_STOPPED;
if (attributes == null) {
@@ -414,15 +445,21 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
audioBuffSizeCheck(bufferSizeInBytes);
+ Identity identity = myIdentity(context);
+ if (identity.packageName == null) {
+ // Command line utility
+ identity.packageName = "uid:" + Binder.getCallingUid();
+ }
+
int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
//TODO: update native initialization when information about hardware init failure
// due to capture device already open is available.
- int initResult = native_setup( new WeakReference<AudioRecord>(this),
+ int initResult = native_setup(new WeakReference<AudioRecord>(this),
mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
- session, getCurrentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
+ session, identity, 0 /*nativeRecordInJavaObj*/);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing native AudioRecord object.");
return; // with mState == STATE_UNINITIALIZED
@@ -434,15 +471,6 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
mState = STATE_INITIALIZED;
}
- private String getCurrentOpPackageName() {
- String opPackageName = ActivityThread.currentOpPackageName();
- if (opPackageName != null) {
- return opPackageName;
- }
- // Command line utility
- return "uid:" + Binder.getCallingUid();
- }
-
/**
* A constructor which explicitly connects a Native (C++) AudioRecord. For use by
* the AudioRecordRoutingProxy subclass.
@@ -492,7 +520,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
0 /*mAudioFormat*/,
0 /*mNativeBufferSizeInBytes*/,
session,
- ActivityThread.currentOpPackageName(),
+ myIdentity(null),
nativeRecordInJavaObj);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing native AudioRecord object.");
@@ -548,6 +576,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
private AudioPlaybackCaptureConfiguration mAudioPlaybackCaptureConfiguration;
private AudioAttributes mAttributes;
private AudioFormat mFormat;
+ private Context mContext;
private int mBufferSizeInBytes;
private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
@@ -583,6 +612,18 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
}
/**
+ * Sets the context the record belongs to.
+ * @param context a non-null {@link Context} instance
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setContext(@NonNull Context context) {
+ Objects.requireNonNull(context);
+ // keep reference, we only copy the data when building
+ mContext = context;
+ return this;
+ }
+
+ /**
* @hide
* To be only used by system components. Allows specifying non-public capture presets
* @param attributes a non-null {@link AudioAttributes} instance that contains the capture
@@ -793,7 +834,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
* mFormat.getBytesPerSample(mFormat.getEncoding());
}
final AudioRecord record = new AudioRecord(
- mAttributes, mFormat, mBufferSizeInBytes, mSessionId);
+ mAttributes, mFormat, mBufferSizeInBytes, mSessionId, mContext);
if (record.getState() == STATE_UNINITIALIZED) {
// release is not necessary
throw new UnsupportedOperationException("Cannot create AudioRecord");
@@ -2035,15 +2076,32 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
// Native methods called from the Java side
//--------------------
- @UnsupportedAppUsage
- private native final int native_setup(Object audiorecord_this,
+ /**
+ * @deprecated Use native_setup that takes an Identity object
+ * @return
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
+ publicAlternatives = "{@code AudioRecord.Builder}")
+ @Deprecated
+ private int native_setup(Object audiorecordThis,
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
int buffSizeInBytes, int[] sessionId, String opPackageName,
- long nativeRecordInJavaObj);
+ long nativeRecordInJavaObj) {
+ Identity identity = myIdentity(null);
+ identity.packageName = opPackageName;
+
+ return native_setup(audiorecordThis, attributes, sampleRate, channelMask, channelIndexMask,
+ audioFormat, buffSizeInBytes, sessionId, identity, nativeRecordInJavaObj);
+ }
+
+ private native int native_setup(Object audiorecordThis,
+ Object /*AudioAttributes*/ attributes,
+ int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+ int buffSizeInBytes, int[] sessionId, Identity identity, long nativeRecordInJavaObj);
// TODO remove: implementation calls directly into implementation of native_release()
- private native final void native_finalize();
+ private native void native_finalize();
/**
* @hide
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f1f6cb1f42be..8134d6f8352d 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1699,8 +1699,10 @@ public class AudioSystem
public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
/** @hide */
- public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats,
- boolean reported);
+ public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats);
+
+ /** @hide */
+ public static native int getReportedSurroundFormats(ArrayList<Integer> surroundFormats);
/**
* @hide
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index b0736389b906..1b743679e585 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -385,8 +385,7 @@ public class ImageWriter implements AutoCloseable {
* (acquired via {@link #dequeueInputImage}). In the former case, the Image
* data will be moved to this ImageWriter. Note that the Image properties
* (size, format, strides, etc.) must be the same as the properties of the
- * images dequeued from this ImageWriter, or this method will throw an
- * {@link IllegalArgumentException}. In the latter case, the application has
+ * images dequeued from this ImageWriter. In the latter case, the application has
* filled the input image with data. This method then passes the filled
* buffer to the downstream consumer. In both cases, it's up to the caller
* to ensure that the Image timestamp (in nanoseconds) is correctly set, as
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 548b415fce74..ae64c026fb51 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -21,7 +21,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
@@ -2937,9 +2936,7 @@ public final class MediaDrm implements AutoCloseable {
* @return a {@link PlaybackComponent} associated with the session,
* or {@code null} if the session is closed or does not exist.
* @see PlaybackComponent
- * @hide
*/
- @TestApi
@Nullable
public PlaybackComponent getPlaybackComponent(@NonNull byte[] sessionId) {
if (sessionId == null) {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index f8a642a68dd7..4e8a273ecc9e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -161,6 +161,10 @@ public final class MediaFormat {
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";
+ /** MIME type for MPEG-H Audio single stream */
+ public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
+ /** MIME type for MPEG-H Audio single stream, encapsulated in MHAS */
+ public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
/**
* MIME type for HEIF still image data encoded in HEVC.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 9176dae8609f..3de78bb9ef9f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -18,6 +18,7 @@ package android.media;
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.permission.PermissionUtil.myIdentity;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -34,6 +35,7 @@ import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
import android.media.SubtitleController.Anchor;
import android.media.SubtitleTrack.RenderingWidget;
+import android.media.permission.Identity;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -53,6 +55,7 @@ import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -684,11 +687,14 @@ public class MediaPlayer extends PlayerBase
mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
+ Identity identity = myIdentity(null);
+ // set the package name to empty if it was null
+ identity.packageName = TextUtils.emptyIfNull(identity.packageName);
+
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
- native_setup(new WeakReference<MediaPlayer>(this),
- getCurrentOpPackageName());
+ native_setup(new WeakReference<MediaPlayer>(this), identity);
baseRegisterPlayer(sessionId);
}
@@ -2471,7 +2477,7 @@ public class MediaPlayer extends PlayerBase
private native final int native_setMetadataFilter(Parcel request);
private static native final void native_init();
- private native void native_setup(Object mediaplayerThis, @NonNull String opPackageName);
+ private native void native_setup(Object mediaplayerThis, @NonNull Identity identity);
private native final void native_finalize();
/**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 49a4cc6239bb..87e1e5bdb9ce 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,6 +16,8 @@
package android.media;
+import static android.media.permission.PermissionUtil.myIdentity;
+
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -25,7 +27,9 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.hardware.Camera;
+import android.media.permission.Identity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -48,6 +52,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -127,9 +132,21 @@ public class MediaRecorder implements AudioRouting,
/**
* Default constructor.
+ *
+ * @deprecated Use {@link #MediaRecorder(Context)} instead
*/
+ @Deprecated
public MediaRecorder() {
+ this(ActivityThread.currentApplication());
+ }
+ /**
+ * Default constructor.
+ *
+ * @param context Context the recorder belongs to
+ */
+ public MediaRecorder(@NonNull Context context) {
+ Objects.requireNonNull(context);
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
@@ -140,12 +157,11 @@ public class MediaRecorder implements AudioRouting,
}
mChannelCount = 1;
- String packageName = ActivityThread.currentPackageName();
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
- native_setup(new WeakReference<MediaRecorder>(this), packageName,
- ActivityThread.currentOpPackageName());
+ native_setup(new WeakReference<MediaRecorder>(this),
+ ActivityThread.currentPackageName(), myIdentity(context));
}
/**
@@ -1740,12 +1756,22 @@ public class MediaRecorder implements AudioRouting,
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static native final void native_init();
- @UnsupportedAppUsage
- private native final void native_setup(Object mediarecorder_this,
- String clientName, String opPackageName) throws IllegalStateException;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
+ publicAlternatives = "{@link MediaRecorder}")
+ private void native_setup(Object mediarecorderThis,
+ String clientName, String opPackageName) throws IllegalStateException {
+ Identity identity = myIdentity(null);
+ identity.packageName = opPackageName;
+
+ native_setup(mediarecorderThis, clientName, identity);
+ }
+
+ private native void native_setup(Object mediarecorderThis,
+ String clientName, Identity identity)
+ throws IllegalStateException;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private native final void native_finalize();
+ private native void native_finalize();
@UnsupportedAppUsage
private native void setParameter(String nameValuePair);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index dc9c58ebf18c..02fa0401e586 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -21,6 +21,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -60,6 +61,7 @@ import java.util.stream.Collectors;
public final class MediaRouter2 {
private static final String TAG = "MR2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final Object sSystemRouterLock = new Object();
private static final Object sRouterLock = new Object();
// The maximum time for the old routing controller available after transfer.
@@ -67,8 +69,8 @@ public final class MediaRouter2 {
// The manager request ID representing that no manager is involved.
private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE;
- @GuardedBy("sRouterLock")
- private static Map<String, MediaRouter2> sMediaRouter2Map = new ArrayMap<>();
+ @GuardedBy("sSystemRouterLock")
+ private static Map<String, MediaRouter2> sSystemMediaRouter2Map = new ArrayMap<>();
private static MediaRouter2Manager sManager;
@GuardedBy("sRouterLock")
@@ -76,6 +78,7 @@ public final class MediaRouter2 {
private final Context mContext;
private final IMediaRouterService mMediaRouterService;
+ private final Object mLock = new Object();
private final CopyOnWriteArrayList<RouteCallbackRecord> mRouteCallbackRecords =
new CopyOnWriteArrayList<>();
@@ -87,28 +90,31 @@ public final class MediaRouter2 {
private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests =
new CopyOnWriteArrayList<>();
+ // TODO: Specify the fields that are only used (or not used) by system media router.
private final String mClientPackageName;
+ private final ManagerCallback mManagerCallback;
+
private final String mPackageName;
- @GuardedBy("sRouterLock")
+ @GuardedBy("mLock")
final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>();
final RoutingController mSystemController;
- @GuardedBy("sRouterLock")
+ @GuardedBy("mLock")
private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
// TODO: Make MediaRouter2 is always connected to the MediaRouterService.
- @GuardedBy("sRouterLock")
+ @GuardedBy("mLock")
MediaRouter2Stub mStub;
- @GuardedBy("sRouterLock")
+ @GuardedBy("mLock")
private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>();
private final AtomicInteger mNextRequestId = new AtomicInteger(1);
final Handler mHandler;
- @GuardedBy("sRouterLock")
+ @GuardedBy("mLock")
private boolean mShouldUpdateRoutes = true;
private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
private volatile OnGetControllerHintsListener mOnGetControllerHintsListener;
@@ -128,13 +134,34 @@ public final class MediaRouter2 {
}
/**
- * Gets an instance of the media router which controls the app's media routing.
+ * Gets an instance of the system media router which controls the app's media routing.
* Returns {@code null} if the given package name is invalid.
+ * There are several things to note when using the media routers created with this method.
+ * <p>
+ * First of all, the discovery preference passed to {@link #registerRouteCallback}
+ * will have no effect. The callback will be called accordingly with the client app's
+ * discovery preference. Therefore, it is recommended to pass
+ * {@link RouteDiscoveryPreference#EMPTY} there.
+ * <p>
+ * Also, do not keep/compare the instances of the {@link RoutingController}, since they are
+ * always newly created with the latest session information whenever below methods are called:
+ * <ul>
+ * <li> {@link #getControllers()} </li>
+ * <li> {@link #getController(String)}} </li>
+ * <li> {@link TransferCallback#onTransfer(RoutingController, RoutingController)} </li>
+ * <li> {@link TransferCallback#onStop(RoutingController)} </li>
+ * <li> {@link ControllerCallback#onControllerUpdated(RoutingController)} </li>
+ * </ul>
+ * Therefore, in order to track the current routing status, keep the controller's ID instead,
+ * and use {@link #getController(String)} and {@link #getSystemController()} for
+ * getting controllers.
+ * <p>
+ * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}.
*
* @param clientPackageName the package name of the app to control
* @hide
*/
- //@SystemApi
+ @SystemApi
@Nullable
public static MediaRouter2 getInstance(@NonNull Context context,
@NonNull String clientPackageName) {
@@ -149,20 +176,50 @@ public final class MediaRouter2 {
return null;
}
- synchronized (sRouterLock) {
- MediaRouter2 instance = sMediaRouter2Map.get(clientPackageName);
+ synchronized (sSystemRouterLock) {
+ MediaRouter2 instance = sSystemMediaRouter2Map.get(clientPackageName);
if (instance == null) {
// TODO: Add permission check here using MODIFY_AUDIO_ROUTING.
if (sManager == null) {
sManager = MediaRouter2Manager.getInstance(context.getApplicationContext());
}
instance = new MediaRouter2(context, clientPackageName);
- sMediaRouter2Map.put(clientPackageName, instance);
+ sSystemMediaRouter2Map.put(clientPackageName, instance);
+ // TODO: Remove router instance once it is not needed.
+ instance.registerManagerCallbackForSystemRouter();
}
return instance;
}
}
+ /**
+ * Starts scanning remote routes.
+ * Note that calling start/stopScan is applied to all system routers in the same process.
+ *
+ * @see #stopScan()
+ * @hide
+ */
+ @SystemApi
+ public void startScan() {
+ if (isSystemRouter()) {
+ sManager.startScan();
+ }
+ }
+
+ /**
+ * Stops scanning remote routes to reduce resource consumption.
+ * Note that calling start/stopScan is applied to all system routers in the same process.
+ *
+ * @see #startScan()
+ * @hide
+ */
+ @SystemApi
+ public void stopScan() {
+ if (isSystemRouter()) {
+ sManager.stopScan();
+ }
+ }
+
private MediaRouter2(Context appContext) {
mContext = appContext;
mMediaRouterService = IMediaRouterService.Stub.asInterface(
@@ -192,16 +249,25 @@ public final class MediaRouter2 {
}
mSystemController = new SystemRoutingController(currentSystemSessionInfo);
+ // Only used by system MediaRouter2.
mClientPackageName = null;
+ mManagerCallback = null;
}
private MediaRouter2(Context context, String clientPackageName) {
- mClientPackageName = clientPackageName;
mContext = context;
- mMediaRouterService = null;
- mPackageName = null;
+ mClientPackageName = clientPackageName;
+ mManagerCallback = new ManagerCallback();
mHandler = new Handler(Looper.getMainLooper());
- mSystemController = null;
+ mSystemController = new SystemRoutingController(
+ ensureClientPackageNameForSystemSession(sManager.getSystemRoutingSession()));
+ mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+ sManager.getPreferredFeatures(clientPackageName), true).build();
+ updateAllRoutesFromManager();
+ mMediaRouterService = null; // TODO: Make this non-null and check permission.
+
+ // Only used by non-system MediaRouter2.
+ mPackageName = null;
}
/**
@@ -220,13 +286,13 @@ public final class MediaRouter2 {
}
/**
- * Gets the target package name of the app which this media router controls.
- * This is only non-null when the router instance is created with the target package name.
+ * Gets the client package name of the app which this media router controls.
+ * This is only non-null when the router instance is created with the client package name.
*
* @see #getInstance(Context, String)
* @hide
*/
- //@SystemApi
+ @SystemApi
@Nullable
public String getClientPackageName() {
return mClientPackageName;
@@ -245,6 +311,9 @@ public final class MediaRouter2 {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(routeCallback, "callback must not be null");
Objects.requireNonNull(preference, "preference must not be null");
+ if (isSystemRouter()) {
+ preference = RouteDiscoveryPreference.EMPTY;
+ }
RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, preference);
@@ -253,7 +322,11 @@ public final class MediaRouter2 {
// is happening but it's okay because either this or the other registration should be done.
mRouteCallbackRecords.addIfAbsent(record);
- synchronized (sRouterLock) {
+ if (isSystemRouter()) {
+ return;
+ }
+
+ synchronized (mLock) {
if (mStub == null) {
MediaRouter2Stub stub = new MediaRouter2Stub();
try {
@@ -289,7 +362,11 @@ public final class MediaRouter2 {
return;
}
- synchronized (sRouterLock) {
+ if (isSystemRouter()) {
+ return;
+ }
+
+ synchronized (mLock) {
if (mStub == null) {
return;
}
@@ -326,22 +403,34 @@ public final class MediaRouter2 {
}
/**
+ * Gets the list of all discovered routes.
+ * This list includes the routes that are not related to the client app.
+ * <p>
+ * This will return an empty list for non-system media routers.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public List<MediaRoute2Info> getAllRoutes() {
+ if (isSystemRouter()) {
+ return sManager.getAllRoutes();
+ }
+ return Collections.emptyList();
+ }
+
+ /**
* Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
* known to the media router.
* <p>
* Please note that the list can be changed before callbacks are invoked.
* </p>
- *
* @return the list of routes that contains at least one of the route features in discovery
* preferences registered by the application
*/
@NonNull
public List<MediaRoute2Info> getRoutes() {
- if (mClientPackageName != null) {
- return sManager.getAvailableRoutes(mClientPackageName);
- }
-
- synchronized (sRouterLock) {
+ synchronized (mLock) {
if (mShouldUpdateRoutes) {
mShouldUpdateRoutes = false;
@@ -434,6 +523,9 @@ public final class MediaRouter2 {
* {@code null} for unset.
*/
public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) {
+ if (isSystemRouter()) {
+ return;
+ }
mOnGetControllerHintsListener = listener;
}
@@ -449,7 +541,7 @@ public final class MediaRouter2 {
* @see TransferCallback#onTransferFailure
*/
public void transferTo(@NonNull MediaRoute2Info route) {
- if (mClientPackageName != null) {
+ if (isSystemRouter()) {
sManager.selectRoute(mClientPackageName, route);
return;
}
@@ -464,7 +556,7 @@ public final class MediaRouter2 {
* controls the media routing, this method is a no-op.
*/
public void stop() {
- if (mClientPackageName != null) {
+ if (isSystemRouter()) {
List<RoutingSessionInfo> sessionInfos = sManager.getRoutingSessions(mClientPackageName);
RoutingSessionInfo sessionToRelease = sessionInfos.get(sessionInfos.size() - 1);
sManager.releaseSession(sessionToRelease);
@@ -479,9 +571,9 @@ public final class MediaRouter2 {
* @param route the route you want to transfer the media to.
* @hide
*/
- //@SystemApi
+ @SystemApi
public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
- if (mClientPackageName != null) {
+ if (isSystemRouter()) {
sManager.transfer(controller.getRoutingSessionInfo(), route);
return;
}
@@ -490,7 +582,7 @@ public final class MediaRouter2 {
Objects.requireNonNull(route, "route must not be null");
boolean routeFound;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
// TODO: Check thread-safety
routeFound = mRoutes.containsKey(route.getId());
}
@@ -526,7 +618,7 @@ public final class MediaRouter2 {
}
MediaRouter2Stub stub;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
@@ -566,6 +658,23 @@ public final class MediaRouter2 {
}
/**
+ * Gets a {@link RoutingController} whose ID is equal to the given ID.
+ * Returns {@code null} if there is no matching controller.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public RoutingController getController(@NonNull String id) {
+ Objects.requireNonNull(id, "id must not be null");
+ for (RoutingController controller : getControllers()) {
+ if (TextUtils.equals(id, controller.getId())) {
+ return controller;
+ }
+ }
+ return null;
+ }
+
+ /**
* Gets the list of currently active {@link RoutingController routing controllers} on which
* media can be played.
* <p>
@@ -574,17 +683,28 @@ public final class MediaRouter2 {
*/
@NonNull
public List<RoutingController> getControllers() {
- // TODO: Do not create the controller instances every time,
- // Instead, update the list using the sessions' ID and session related callbacks.
- if (mClientPackageName != null) {
- return sManager.getRoutingSessions(mClientPackageName).stream()
- .map(info -> new RoutingController(info))
- .collect(Collectors.toList());
+ List<RoutingController> result = new ArrayList<>();
+
+ if (isSystemRouter()) {
+ // Unlike non-system MediaRouter2, controller instances cannot be kept,
+ // since the transfer events initiated from other apps will not come through manager.
+ List<RoutingSessionInfo> sessions = sManager.getRoutingSessions(mClientPackageName);
+ for (RoutingSessionInfo session : sessions) {
+ RoutingController controller;
+ if (session.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(
+ ensureClientPackageNameForSystemSession(session));
+ controller = mSystemController;
+ } else {
+ controller = new RoutingController(session);
+ }
+ result.add(controller);
+ }
+ return result;
}
- List<RoutingController> result = new ArrayList<>();
result.add(0, mSystemController);
- synchronized (sRouterLock) {
+ synchronized (mLock) {
result.addAll(mNonSystemRoutingControllers.values());
}
return result;
@@ -599,11 +719,17 @@ public final class MediaRouter2 {
* @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
* @hide
*/
+ @SystemApi
public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
Objects.requireNonNull(route, "route must not be null");
+ if (isSystemRouter()) {
+ sManager.setRouteVolume(route, volume);
+ return;
+ }
+
MediaRouter2Stub stub;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
@@ -627,7 +753,7 @@ public final class MediaRouter2 {
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
- synchronized (sRouterLock) {
+ synchronized (mLock) {
List<String> currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId)
.collect(Collectors.toList());
@@ -685,7 +811,7 @@ public final class MediaRouter2 {
void addRoutesOnHandler(List<MediaRoute2Info> routes) {
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
- synchronized (sRouterLock) {
+ synchronized (mLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
@@ -701,7 +827,7 @@ public final class MediaRouter2 {
void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
- synchronized (sRouterLock) {
+ synchronized (mLock) {
for (MediaRoute2Info route : routes) {
mRoutes.remove(route.getId());
if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
@@ -717,7 +843,7 @@ public final class MediaRouter2 {
void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
- synchronized (sRouterLock) {
+ synchronized (mLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
@@ -789,7 +915,7 @@ public final class MediaRouter2 {
newController.setRoutingSessionInfo(sessionInfo);
} else {
newController = new RoutingController(sessionInfo);
- synchronized (sRouterLock) {
+ synchronized (mLock) {
mNonSystemRoutingControllers.put(newController.getId(), newController);
}
}
@@ -812,7 +938,7 @@ public final class MediaRouter2 {
}
RoutingController matchingController;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
}
@@ -840,7 +966,7 @@ public final class MediaRouter2 {
}
RoutingController matchingController;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
}
@@ -868,7 +994,7 @@ public final class MediaRouter2 {
if (oldSession.isSystemSession()) {
controller = getSystemController();
} else {
- synchronized (sRouterLock) {
+ synchronized (mLock) {
controller = mNonSystemRoutingControllers.get(oldSession.getId());
}
}
@@ -878,6 +1004,40 @@ public final class MediaRouter2 {
requestCreateController(controller, route, managerRequestId);
}
+ /**
+ * Returns whether this router is created with {@link #getInstance(Context, String)}.
+ * This kind of router can control the target app's media routing.
+ */
+ private boolean isSystemRouter() {
+ return mClientPackageName != null;
+ }
+
+ /**
+ * Registers {@link MediaRouter2Manager.Callback} for getting events.
+ * Should only used for system media routers.
+ */
+ private void registerManagerCallbackForSystemRouter() {
+ // Using direct executor here, since MediaRouter2Manager also posts to the main handler.
+ sManager.registerCallback(Runnable::run, mManagerCallback);
+ }
+
+ /**
+ * Returns a {@link RoutingSessionInfo} which has the client package name.
+ * The client package name is set only when the given sessionInfo doesn't have it.
+ * Should only used for system media routers.
+ */
+ private RoutingSessionInfo ensureClientPackageNameForSystemSession(
+ @NonNull RoutingSessionInfo sessionInfo) {
+ if (!sessionInfo.isSystemSession()
+ || !TextUtils.isEmpty(sessionInfo.getClientPackageName())) {
+ return sessionInfo;
+ }
+
+ return new RoutingSessionInfo.Builder(sessionInfo)
+ .setClientPackageName(mClientPackageName)
+ .build();
+ }
+
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
RouteDiscoveryPreference discoveryRequest) {
return routes.stream()
@@ -885,6 +1045,16 @@ public final class MediaRouter2 {
.collect(Collectors.toList());
}
+ private void updateAllRoutesFromManager() {
+ synchronized (mLock) {
+ mRoutes.clear();
+ for (MediaRoute2Info route : sManager.getAllRoutes()) {
+ mRoutes.put(route.getId(), route);
+ }
+ mShouldUpdateRoutes = true;
+ }
+ }
+
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
for (RouteCallbackRecord record: mRouteCallbackRecords) {
List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
@@ -915,6 +1085,13 @@ public final class MediaRouter2 {
}
}
+ private void notifyPreferredFeaturesChanged(List<String> features) {
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mRouteCallback.onPreferredFeaturesChanged(features));
+ }
+ }
+
private void notifyTransfer(RoutingController oldController, RoutingController newController) {
for (TransferCallbackRecord record: mTransferCallbackRecords) {
record.mExecutor.execute(
@@ -968,6 +1145,17 @@ public final class MediaRouter2 {
* @param routes the list of routes that have been changed. It's never empty.
*/
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
+
+ /**
+ * Called when the client app's preferred features are changed.
+ * When this is called, it is recommended to {@link #getRoutes()} to get the routes
+ * that are currently available to the app.
+ *
+ * @param preferredFeatures the new preferred features set by the application
+ * @hide
+ */
+ @SystemApi
+ public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {}
}
/**
@@ -1075,6 +1263,11 @@ public final class MediaRouter2 {
mState = CONTROLLER_STATE_ACTIVE;
}
+ RoutingController(@NonNull RoutingSessionInfo sessionInfo, int state) {
+ mSessionInfo = sessionInfo;
+ mState = state;
+ }
+
/**
* @return the ID of the controller. It is globally unique.
*/
@@ -1235,8 +1428,13 @@ public final class MediaRouter2 {
return;
}
+ if (isSystemRouter()) {
+ sManager.selectRoute(getRoutingSessionInfo(), route);
+ return;
+ }
+
MediaRouter2Stub stub;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
@@ -1282,8 +1480,13 @@ public final class MediaRouter2 {
return;
}
+ if (isSystemRouter()) {
+ sManager.deselectRoute(getRoutingSessionInfo(), route);
+ return;
+ }
+
MediaRouter2Stub stub;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
@@ -1318,7 +1521,7 @@ public final class MediaRouter2 {
}
MediaRouter2Stub stub;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
@@ -1351,8 +1554,14 @@ public final class MediaRouter2 {
Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
return;
}
+
+ if (isSystemRouter()) {
+ sManager.setSessionVolume(getRoutingSessionInfo(), volume);
+ return;
+ }
+
MediaRouter2Stub stub;
- synchronized (sRouterLock) {
+ synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
@@ -1386,7 +1595,7 @@ public final class MediaRouter2 {
mState = CONTROLLER_STATE_RELEASING;
}
- synchronized (sRouterLock) {
+ synchronized (mLock) {
// It could happen if the controller is released by the another thread
// in between two locks
if (!mNonSystemRoutingControllers.remove(getId(), this)) {
@@ -1415,7 +1624,12 @@ public final class MediaRouter2 {
mState = CONTROLLER_STATE_RELEASED;
}
- synchronized (sRouterLock) {
+ if (isSystemRouter()) {
+ sManager.releaseSession(getRoutingSessionInfo());
+ return;
+ }
+
+ synchronized (mLock) {
mNonSystemRoutingControllers.remove(getId(), this);
if (shouldReleaseSession && mStub != null) {
@@ -1483,7 +1697,13 @@ public final class MediaRouter2 {
}
private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
- synchronized (sRouterLock) {
+ if (isSystemRouter()) {
+ return getRoutes().stream()
+ .filter(r -> routeIds.contains(r.getId()))
+ .collect(Collectors.toList());
+ }
+
+ synchronized (mLock) {
return routeIds.stream().map(mRoutes::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
@@ -1665,4 +1885,162 @@ public final class MediaRouter2 {
MediaRouter2.this, oldSession, route, managerRequestId));
}
}
+
+ // Note: All methods are run on main thread.
+ class ManagerCallback implements MediaRouter2Manager.Callback {
+
+ @Override
+ public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {
+ updateAllRoutesFromManager();
+
+ List<MediaRoute2Info> filteredRoutes;
+ synchronized (mLock) {
+ filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+ }
+ if (filteredRoutes.isEmpty()) {
+ return;
+ }
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
+ }
+ }
+
+ @Override
+ public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {
+ updateAllRoutesFromManager();
+
+ List<MediaRoute2Info> filteredRoutes;
+ synchronized (mLock) {
+ filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+ }
+ if (filteredRoutes.isEmpty()) {
+ return;
+ }
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
+ }
+ }
+
+ @Override
+ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {
+ updateAllRoutesFromManager();
+
+ List<MediaRoute2Info> filteredRoutes;
+ synchronized (mLock) {
+ filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+ }
+ if (filteredRoutes.isEmpty()) {
+ return;
+ }
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
+ }
+ }
+
+ @Override
+ public void onTransferred(@NonNull RoutingSessionInfo oldSession,
+ @NonNull RoutingSessionInfo newSession) {
+ if (!oldSession.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) {
+ return;
+ }
+
+ if (!newSession.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, newSession.getClientPackageName())) {
+ return;
+ }
+
+ // For successful in-session transfer, onControllerUpdated() handles it.
+ if (TextUtils.equals(oldSession.getId(), newSession.getId())) {
+ return;
+ }
+
+
+ RoutingController oldController;
+ if (oldSession.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(
+ ensureClientPackageNameForSystemSession(oldSession));
+ oldController = mSystemController;
+ } else {
+ oldController = new RoutingController(oldSession);
+ }
+
+ RoutingController newController;
+ if (newSession.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(
+ ensureClientPackageNameForSystemSession(newSession));
+ newController = mSystemController;
+ } else {
+ newController = new RoutingController(newSession);
+ }
+
+ notifyTransfer(oldController, newController);
+ }
+
+ @Override
+ public void onTransferFailed(@NonNull RoutingSessionInfo session,
+ @NonNull MediaRoute2Info route) {
+ if (!session.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+ return;
+ }
+ notifyTransferFailure(route);
+ }
+
+ @Override
+ public void onSessionUpdated(@NonNull RoutingSessionInfo session) {
+ if (!session.isSystemSession()
+ && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+ return;
+ }
+
+ RoutingController controller;
+ if (session.isSystemSession()) {
+ mSystemController.setRoutingSessionInfo(
+ ensureClientPackageNameForSystemSession(session));
+ controller = mSystemController;
+ } else {
+ controller = new RoutingController(session);
+ }
+ notifyControllerUpdated(controller);
+ }
+
+ @Override
+ public void onSessionReleased(@NonNull RoutingSessionInfo session) {
+ if (session.isSystemSession()) {
+ Log.e(TAG, "onSessionReleased: Called on system session. Ignoring.");
+ return;
+ }
+
+ if (!TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+ return;
+ }
+
+ notifyStop(new RoutingController(session, RoutingController.CONTROLLER_STATE_RELEASED));
+ }
+
+ @Override
+ public void onPreferredFeaturesChanged(@NonNull String packageName,
+ @NonNull List<String> preferredFeatures) {
+ if (!TextUtils.equals(mClientPackageName, packageName)) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+ preferredFeatures, true).build();
+ }
+
+ updateAllRoutesFromManager();
+ notifyPreferredFeaturesChanged(preferredFeatures);
+ }
+
+ @Override
+ public void onRequestFailed(int reason) {
+ // Does nothing.
+ }
+ }
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5e732f9be68f..6fefbe15abae 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -148,7 +148,7 @@ public final class MediaRouter2Manager {
/**
* Starts scanning remote routes.
- * @see #stopScan(String)
+ * @see #stopScan()
*/
public void startScan() {
Client client = getOrCreateClient();
@@ -163,7 +163,7 @@ public final class MediaRouter2Manager {
/**
* Stops scanning remote routes to reduce resource consumption.
- * @see #startScan(String)
+ * @see #startScan()
*/
public void stopScan() {
Client client = getOrCreateClient();
@@ -237,6 +237,50 @@ public final class MediaRouter2Manager {
}
/**
+ * Returns the preferred features of the specified package name.
+ */
+ @NonNull
+ public List<String> getPreferredFeatures(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+ if (preferredFeatures == null) {
+ preferredFeatures = Collections.emptyList();
+ }
+ return preferredFeatures;
+ }
+
+ /**
+ * Returns a list of routes which are related to the given package name in the given route list.
+ */
+ @NonNull
+ public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes,
+ @NonNull String packageName) {
+ Objects.requireNonNull(routes, "routes must not be null");
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ List<RoutingSessionInfo> sessions = getRoutingSessions(packageName);
+ RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1);
+
+ List<MediaRoute2Info> result = new ArrayList<>();
+ List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+ if (preferredFeatures == null) {
+ preferredFeatures = Collections.emptyList();
+ }
+
+ synchronized (mRoutesLock) {
+ for (MediaRoute2Info route : routes) {
+ if (route.hasAnyFeatures(preferredFeatures)
+ || sessionInfo.getSelectedRoutes().contains(route.getId())
+ || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ result.add(route);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
* Gets the system routing session associated with no specific application.
*/
@NonNull
@@ -758,8 +802,8 @@ public final class MediaRouter2Manager {
* Requests releasing a session.
* <p>
* If a session is released, any operation on the session will be ignored.
- * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null}
- * session will be called when the session is released.
+ * {@link Callback#onSessionReleased(RoutingSessionInfo)} will be called
+ * when the session is released.
* </p>
*
* @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
@@ -915,10 +959,10 @@ public final class MediaRouter2Manager {
* Called when media is transferred.
*
* @param oldSession the previous session
- * @param newSession the new session or {@code null} if the session is released.
+ * @param newSession the new session
*/
default void onTransferred(@NonNull RoutingSessionInfo oldSession,
- @Nullable RoutingSessionInfo newSession) { }
+ @NonNull RoutingSessionInfo newSession) { }
/**
* Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails.
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 67a4a4b1f851..fd3c4057ad21 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -16,6 +16,8 @@
package android.media.audiofx;
+import static android.media.permission.PermissionUtil.myIdentity;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -23,11 +25,11 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioSystem;
+import android.media.permission.Identity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -515,10 +517,11 @@ public class AudioEffect {
}
// native initialization
+ // TODO b/182469354: Make consistent with AudioRecord
int initResult = native_setup(new WeakReference<AudioEffect>(this),
type.toString(), uuid.toString(), priority, audioSession,
deviceType, deviceAddress,
- id, desc, ActivityThread.currentOpPackageName(), probe);
+ id, desc, myIdentity(null), probe);
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
Log.e(TAG, "Error code " + initResult
+ " when initializing AudioEffect.");
@@ -1385,7 +1388,7 @@ public class AudioEffect {
private native final int native_setup(Object audioeffect_this, String type,
String uuid, int priority, int audioSession,
int deviceType, String deviceAddress, int[] id, Object[] desc,
- String opPackageName, boolean probe);
+ Identity identity, boolean probe);
private native final void native_finalize();
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index a5da648cf14a..58c9e650bb90 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -16,8 +16,10 @@
package android.media.audiofx;
-import android.app.ActivityThread;
+import static android.media.permission.PermissionUtil.myIdentity;
+
import android.compat.annotation.UnsupportedAppUsage;
+import android.media.permission.Identity;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
@@ -217,9 +219,11 @@ public class Visualizer {
synchronized (mStateLock) {
mState = STATE_UNINITIALIZED;
+
// native initialization
+ // TODO b/182469354: make consistent with AudioRecord
int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
- ActivityThread.currentOpPackageName());
+ myIdentity(null));
if (result != SUCCESS && result != ALREADY_EXISTS) {
Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
switch (result) {
@@ -686,7 +690,7 @@ public class Visualizer {
private native final int native_setup(Object audioeffect_this,
int audioSession,
int[] id,
- String opPackageName);
+ Identity identity);
@GuardedBy("mStateLock")
private native final void native_finalize();
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
index 098885cc9bdd..186606825104 100644
--- a/media/java/android/media/metrics/NetworkEvent.java
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -113,10 +113,11 @@ public final class NetworkEvent extends Event implements Parcelable {
*
* @hide
*/
- public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis, Bundle extras) {
+ public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis,
+ @Nullable Bundle extras) {
this.mNetworkType = type;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras.deepCopy();
+ this.mExtras = extras == null ? null : extras.deepCopy();
}
/**
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
index b23b4d2728b4..ccf848b50a36 100644
--- a/media/java/android/media/metrics/PlaybackErrorEvent.java
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -65,12 +65,12 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
int errorCode,
int subErrorCode,
long timeSinceCreatedMillis,
- Bundle extras) {
+ @Nullable Bundle extras) {
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras.deepCopy();
+ this.mExtras = extras == null ? null : extras.deepCopy();
}
/** @hide */
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 7e7f44a97b9c..3ffd10f4ea8c 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -194,7 +194,7 @@ public final class PlaybackMetrics implements Parcelable {
long localBytesRead,
long networkTransferDurationMillis,
byte[] drmSessionId,
- Bundle extras) {
+ @Nullable Bundle extras) {
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
this.mStreamType = streamType;
@@ -212,7 +212,7 @@ public final class PlaybackMetrics implements Parcelable {
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
this.mDrmSessionId = drmSessionId;
- this.mExtras = extras.deepCopy();
+ this.mExtras = extras == null ? null : extras.deepCopy();
}
/**
diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java
index dea8c1db71de..2bab5c9bc3dc 100644
--- a/media/java/android/media/metrics/PlaybackStateEvent.java
+++ b/media/java/android/media/metrics/PlaybackStateEvent.java
@@ -138,10 +138,10 @@ public final class PlaybackStateEvent extends Event implements Parcelable {
public PlaybackStateEvent(
int state,
long timeSinceCreatedMillis,
- Bundle extras) {
+ @Nullable Bundle extras) {
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mState = state;
- this.mExtras = extras.deepCopy();
+ this.mExtras = extras == null ? null : extras.deepCopy();
}
/**
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
index aa519782ec80..a3eb4adfeb8d 100644
--- a/media/java/android/media/metrics/TrackChangeEvent.java
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -167,7 +167,7 @@ public final class TrackChangeEvent extends Event implements Parcelable {
this.mWidth = width;
this.mHeight = height;
this.mVideoFrameRate = videoFrameRate;
- this.mExtras = extras.deepCopy();
+ this.mExtras = extras == null ? null : extras.deepCopy();
}
/**
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl
new file mode 100644
index 000000000000..ceef73cd194a
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl
@@ -0,0 +1,10 @@
+package android.media.musicrecognition;
+
+/**
+ * Interface from {@link MusicRecognitionService} to system to pass attribution tag.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionAttributionTagCallback {
+ void onAttributionTag(in String attributionTag);
+} \ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
index 26543ed8bf8c..c970161a115b 100644
--- a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
@@ -4,6 +4,7 @@ import android.media.AudioFormat;
import android.os.ParcelFileDescriptor;
import android.os.IBinder;
import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback;
/**
* Interface from the system to a {@link MusicRecognitionService}.
@@ -15,4 +16,6 @@ oneway interface IMusicRecognitionService {
in ParcelFileDescriptor fd,
in AudioFormat audioFormat,
in IMusicRecognitionServiceCallback callback);
+
+ void getAttributionTag(in IMusicRecognitionAttributionTagCallback callback);
} \ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
index 15215c4e15f1..10a65545cc7e 100644
--- a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
@@ -4,7 +4,7 @@ import android.os.Bundle;
import android.media.MediaMetadata;
/**
- * Interface from a {@MusicRecognitionService} the system.
+ * Interface from a {@MusicRecognitionService} to the system.
*
* @hide
*/
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
index 04b4c39bf0fa..385aff01d45a 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionService.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -90,7 +90,7 @@ public abstract class MusicRecognitionService extends Service {
try {
callback.onRecognitionSucceeded(result, extras);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -99,11 +99,18 @@ public abstract class MusicRecognitionService extends Service {
try {
callback.onRecognitionFailed(failureCode);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}));
}
+
+ @Override
+ public void getAttributionTag(
+ IMusicRecognitionAttributionTagCallback callback) throws RemoteException {
+ String tag = MusicRecognitionService.this.getAttributionTag();
+ callback.onAttributionTag(tag);
+ }
};
@Override
diff --git a/media/java/android/media/permission/PermissionUtil.java b/media/java/android/media/permission/PermissionUtil.java
index 315ee4f1e998..92fe8820570c 100644
--- a/media/java/android/media/permission/PermissionUtil.java
+++ b/media/java/android/media/permission/PermissionUtil.java
@@ -17,9 +17,12 @@
package android.media.permission;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.PermissionChecker;
import android.os.Binder;
+import android.os.Process;
import java.util.Objects;
@@ -49,6 +52,25 @@ import java.util.Objects;
*/
public class PermissionUtil {
/**
+ * Create an identity for the current process and the passed context.
+ *
+ * @param context The process the identity is for. If {@code null}, the process's default
+ * identity is chosen.
+ * @return The identity for the current process and context
+ */
+ public static @NonNull Identity myIdentity(@Nullable Context context) {
+ Identity identity = new Identity();
+
+ identity.pid = Process.myPid();
+ identity.uid = Process.myUid();
+ identity.packageName = context != null ? context.getOpPackageName()
+ : ActivityThread.currentOpPackageName();
+ identity.attributionTag = context != null ? context.getAttributionTag() : null;
+
+ return identity;
+ }
+
+ /**
* Authenticate an originator, where the binder call is coming from a middleman.
*
* The middleman is expected to hold a special permission to act as such, or else a
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ce4550492740..f09dcde1ee28 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -84,6 +84,7 @@ cc_library_shared {
"android.hardware.drm@1.4",
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
+ "media_permission-aidl-cpp",
],
header_libs: [
@@ -182,7 +183,7 @@ cc_library_shared {
"libnativehelper",
"libutils",
"tv_tuner_aidl_interface-ndk_platform",
- "tv_tuner_resource_manager_aidl_interface-ndk_platform"
+ "tv_tuner_resource_manager_aidl_interface-ndk_platform",
],
static_libs: [
@@ -212,4 +213,3 @@ cc_library_shared {
"-Wunreachable-code",
],
}
-
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 1870a939f0dc..7f5dd5d15dbe 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2260,6 +2260,8 @@ static void android_media_MediaCodec_native_queueHardwareBuffer(
c2_status_t c2err = sGrallocAlloc->priorGraphicAllocation(handle, &alloc);
if (c2err != C2_OK) {
ALOGW("Failed to wrap AHardwareBuffer into C2GraphicAllocation");
+ native_handle_close(handle);
+ native_handle_delete(handle);
throwExceptionAsNecessary(env, BAD_VALUE);
return;
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 98ac5b983098..a3607597f05e 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer-JNI"
+#include "permission_utils.h"
#include "utils/Log.h"
#include <media/mediaplayer.h>
@@ -79,6 +80,8 @@ static StateExceptionFields gStateExceptionFields;
using namespace android;
using media::VolumeShaper;
+using media::permission::Identity;
+using media::permission::convertIdentity;
// ----------------------------------------------------------------------------
@@ -946,11 +949,11 @@ android_media_MediaPlayer_native_init(JNIEnv *env)
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jstring opPackageName)
+ jobject jIdentity)
{
ALOGV("native_setup");
- ScopedUtfChars opPackageNameStr(env, opPackageName);
- sp<MediaPlayer> mp = new MediaPlayer(opPackageNameStr.c_str());
+
+ sp<MediaPlayer> mp = new MediaPlayer(convertIdentity(env, jIdentity));
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
@@ -1406,7 +1409,7 @@ static const JNINativeMethod gMethods[] = {
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
+ {"native_setup", "(Ljava/lang/Object;Landroid/media/permission/Identity;)V",(void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index f99dc012be95..66411233216f 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -24,6 +24,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaRecorderJNI"
+#include "permission_utils.h"
#include <utils/Log.h>
#include <gui/Surface.h>
@@ -50,6 +51,8 @@
using namespace android;
+using android::media::permission::convertIdentity;
+
// ----------------------------------------------------------------------------
// helper function to extract a native Camera object from a Camera Java object
@@ -617,13 +620,12 @@ android_media_MediaRecorder_native_init(JNIEnv *env)
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jstring packageName, jstring opPackageName)
+ jstring packageName, jobject jIdentity)
{
ALOGV("setup");
- ScopedUtfChars opPackageNameStr(env, opPackageName);
+ sp<MediaRecorder> mr = new MediaRecorder(convertIdentity(env, jIdentity));
- sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
if (mr == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
@@ -869,7 +871,7 @@ static const JNINativeMethod gMethods[] = {
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
{"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Landroid/media/permission/Identity;)V",
(void *)android_media_MediaRecorder_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
{"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index c2fc91d5cfea..bfed983c8bce 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -27,6 +27,11 @@ cc_library_shared {
"libaudioclient",
"libaudioutils",
"libaudiofoundation",
+ "media_permission-aidl-cpp",
+ ],
+
+ export_shared_lib_headers: [
+ "media_permission-aidl-cpp",
],
version_script: "exports.lds",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index a74ae5307a36..8a52456849f0 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -34,8 +34,8 @@ namespace android {
// ---------------------------------------------------------------------------
-Visualizer::Visualizer (const String16& opPackageName)
- : AudioEffect(opPackageName)
+Visualizer::Visualizer (const Identity& identity)
+ : AudioEffect(identity)
{
}
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index 8b6a62f25638..3ee91f0f8b1e 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,6 +20,9 @@
#include <media/AudioEffect.h>
#include <system/audio_effects/effect_visualizer.h>
#include <utils/Thread.h>
+#include "android/media/permission/Identity.h"
+
+using namespace android::media::permission;
/**
* The Visualizer class enables application to retrieve part of the currently playing audio for
@@ -65,7 +68,7 @@ public:
/* Constructor.
* See AudioEffect constructor for details on parameters.
*/
- explicit Visualizer(const String16& opPackageName);
+ explicit Visualizer(const Identity& identity);
~Visualizer();
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 0d53ab152129..953b7e01c983 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -25,6 +25,7 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "media/AudioEffect.h"
+#include "permission_utils.h"
#include <nativehelper/ScopedUtfChars.h>
@@ -34,6 +35,8 @@
using namespace android;
+using media::permission::convertIdentity;
+
#define AUDIOEFFECT_SUCCESS 0
#define AUDIOEFFECT_ERROR (-1)
#define AUDIOEFFECT_ERROR_ALREADY_EXISTS (-2)
@@ -270,7 +273,7 @@ static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jstring type, jstring uuid, jint priority, jint sessionId,
jint deviceType, jstring deviceAddress,
- jintArray jId, jobjectArray javadesc, jstring opPackageName, jboolean probe)
+ jintArray jId, jobjectArray javadesc, jobject jIdentity, jboolean probe)
{
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
@@ -283,8 +286,6 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
jobject jdesc;
AudioDeviceTypeAddr device;
- ScopedUtfChars opPackageNameStr(env, opPackageName);
-
setAudioEffect(env, thiz, 0);
if (type != NULL) {
@@ -337,7 +338,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
}
// create the native AudioEffect object
- lpAudioEffect = new AudioEffect(String16(opPackageNameStr.c_str()));
+ lpAudioEffect = new AudioEffect(convertIdentity(env, jIdentity));
if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
@@ -773,7 +774,7 @@ android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz _
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Ljava/lang/String;Z)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Landroid/media/permission/Identity;Z)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 4c5970a30a05..439715cbb811 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -25,6 +25,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
#include "Visualizer.h"
+#include "permission_utils.h"
#include <nativehelper/ScopedUtfChars.h>
@@ -347,7 +348,7 @@ static void android_media_visualizer_effect_callback(int32_t event,
static jint
android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint sessionId, jintArray jId, jstring opPackageName)
+ jint sessionId, jintArray jId, jobject jIdentity)
{
ALOGV("android_media_visualizer_native_setup");
VisualizerJniStorage* lpJniStorage = NULL;
@@ -355,8 +356,6 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
sp<Visualizer> lpVisualizer;
jint* nId = NULL;
- ScopedUtfChars opPackageNameStr(env, opPackageName);
-
setVisualizer(env, thiz, 0);
lpJniStorage = new VisualizerJniStorage();
@@ -382,7 +381,7 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
}
// create the native Visualizer object
- lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str()));
+ lpVisualizer = new Visualizer(convertIdentity(env, jIdentity));
if (lpVisualizer == 0) {
ALOGE("Error creating Visualizer");
goto setup_failure;
@@ -679,7 +678,7 @@ android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_visualizer_native_init},
- {"native_setup", "(Ljava/lang/Object;I[ILjava/lang/String;)I",
+ {"native_setup", "(Ljava/lang/Object;I[ILandroid/media/permission/Identity;)I",
(void *)android_media_visualizer_native_setup},
{"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
{"native_release", "()V", (void *)android_media_visualizer_native_release},
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index b3406cd89046..4227cd8cbb29 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -63,7 +63,7 @@ tidy_errors = [
// Remove some pedantic stylistic requirements.
"-google-readability-casting", // C++ casts not always necessary and may be verbose
- "-google-readability-todo", // do not require TODO(info)
+ "-google-readability-todo", // do not require TODO(info)
"-google-build-using-namespace", // Reenable and fix later.
"-google-explicit-constructor", // found in StreamManager.h
@@ -100,7 +100,7 @@ cc_defaults {
tidy_checks: tidy_errors,
tidy_checks_as_errors: tidy_errors,
tidy_flags: [
- "-format-style=file",
+ "-format-style=file",
],
}
@@ -135,6 +135,7 @@ cc_library_shared {
"libaudioclient",
"libmediandk",
"libbinder",
+ "media_permission-aidl-cpp",
],
cflags: [
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
index f8b4bdb1f4d5..50e0d336f981 100644
--- a/media/jni/soundpool/Sound.cpp
+++ b/media/jni/soundpool/Sound.cpp
@@ -99,8 +99,8 @@ static status_t decode(int fd, int64_t offset, int64_t length,
__func__);
break;
}
- int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
- ALOGV("%s: read %d", __func__, sampleSize);
+ ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+ ALOGV("%s: read %zd", __func__, sampleSize);
if (sampleSize < 0) {
sampleSize = 0;
sawInputEOS = true;
@@ -124,8 +124,8 @@ static status_t decode(int fd, int64_t offset, int64_t length,
}
AMediaCodecBufferInfo info;
- const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
- ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+ const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+ ALOGV("%s: dequeueoutput returned: %zd", __func__, status);
if (status >= 0) {
if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
ALOGV("%s: output EOS", __func__);
@@ -167,10 +167,10 @@ static status_t decode(int fd, int64_t offset, int64_t length,
} else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
ALOGV("%s: no output buffer right now", __func__);
} else if (status <= AMEDIA_ERROR_BASE) {
- ALOGE("%s: decode error: %d", __func__, status);
+ ALOGE("%s: decode error: %zd", __func__, status);
break;
} else {
- ALOGV("%s: unexpected info code: %d", __func__, status);
+ ALOGV("%s: unexpected info code: %zd", __func__, status);
}
}
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 73e319a5902e..95fe000bd2c2 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool::Stream"
#include <utils/Log.h>
+#include<android/media/permission/Identity.h>
#include "Stream.h"
@@ -24,6 +25,8 @@
namespace android::soundpool {
+using media::permission::Identity;
+
Stream::~Stream()
{
ALOGV("%s(%p)", __func__, this);
@@ -317,7 +320,8 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
// audio track while the new one is being started and avoids processing them with
// wrong audio audio buffer size (mAudioBufferSize)
auto toggle = mToggle ^ 1;
- void* userData = (void*)((uintptr_t)this | toggle);
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
audio_channel_mask_t soundChannelMask = sound->getChannelMask();
// When sound contains a valid channel mask, use it as is.
// Otherwise, use stream count to calculate channel mask.
@@ -326,15 +330,17 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
// do not create a new audio track if current track is compatible with sound parameters
+ Identity identity = Identity();
+ identity.packageName = mStreamManager->getOpPackageName();
+ // TODO b/182469354 make consistent with AudioRecord, add util for native source
newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
staticCallback, userData,
0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_DEFAULT,
- nullptr /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/,
+ nullptr /*offloadInfo*/, identity,
mStreamManager->getAttributes(),
- false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/,
- mStreamManager->getOpPackageName());
+ false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
// Set caller name so it can be logged in destructor.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
newTrack->setCallerName("soundpool");
@@ -386,6 +392,7 @@ exit:
void Stream::staticCallback(int event, void* user, void* info)
{
const auto userAsInt = (uintptr_t)user;
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
}
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 502ee00b583e..309d71cfd062 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -43,6 +43,14 @@ static constexpr bool kPlayOnCallingThread = true;
// Amount of time for a StreamManager thread to wait before closing.
static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
+// Debug flag:
+// kForceLockStreamManagerStop is set to true to force lock the StreamManager
+// worker thread during stop. This limits concurrency of Stream processing.
+// Normally we lock the StreamManager worker thread during stop ONLY
+// for SoundPools configured with a single Stream.
+//
+static constexpr bool kForceLockStreamManagerStop = false;
+
////////////
StreamMap::StreamMap(int32_t streams) {
@@ -103,6 +111,7 @@ StreamManager::StreamManager(
: StreamMap(streams)
, mAttributes(*attributes)
, mOpPackageName(std::move(opPackageName))
+ , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
{
ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
forEach([this](Stream *stream) {
@@ -113,7 +122,8 @@ StreamManager::StreamManager(
});
mThreadPool = std::make_unique<ThreadPool>(
- std::min(threads, (size_t)std::thread::hardware_concurrency()),
+ std::min((size_t)streams, // do not make more threads than streams to play
+ std::min(threads, (size_t)std::thread::hardware_concurrency())),
"SoundPool_");
}
@@ -330,7 +340,7 @@ ssize_t StreamManager::removeFromQueues_l(
// streams on mProcessingStreams are undergoing processing by the StreamManager thread
// and do not participate in normal stream migration.
- return found;
+ return (ssize_t)found;
}
void StreamManager::addToRestartQueue_l(Stream *stream) {
@@ -348,14 +358,14 @@ void StreamManager::addToActiveQueue_l(Stream *stream) {
void StreamManager::run(int32_t id)
{
ALOGV("%s(%d) entering", __func__, id);
- int64_t waitTimeNs = kWaitTimeBeforeCloseNs;
+ int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty.
std::unique_lock lock(mStreamManagerLock);
while (!mQuit) {
- if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty.
+ if (waitTimeNs > 0) {
mStreamManagerCondition.wait_for(
lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs));
}
- ALOGV("%s(%d) awake", __func__, id);
+ ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs);
sanityCheckQueue_l();
@@ -375,12 +385,12 @@ void StreamManager::run(int32_t id)
}
mRestartStreams.erase(it);
mProcessingStreams.emplace(stream);
- lock.unlock();
+ if (!mLockStreamManagerStop) lock.unlock();
stream->stop();
ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
if (Stream* nextStream = stream->playPairStream()) {
ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
- lock.lock();
+ if (!mLockStreamManagerStop) lock.lock();
if (nextStream->getStopTimeNs() > 0) {
// the next stream was stopped before we can move it to the active queue.
ALOGV("%s(%d) stopping started streamID:%d",
@@ -390,7 +400,7 @@ void StreamManager::run(int32_t id)
addToActiveQueue_l(nextStream);
}
} else {
- lock.lock();
+ if (!mLockStreamManagerStop) lock.lock();
mAvailableStreams.insert(stream);
}
mProcessingStreams.erase(stream);
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
index 81ac69eb4358..85b468cd2cbc 100644
--- a/media/jni/soundpool/StreamManager.h
+++ b/media/jni/soundpool/StreamManager.h
@@ -437,6 +437,14 @@ private:
void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock);
const audio_attributes_t mAttributes;
+ const std::string mOpPackageName;
+
+ // For legacy compatibility, we lock the stream manager on stop when
+ // there is only one stream. This allows a play to be called immediately
+ // after stopping, otherwise it is possible that the play might be discarded
+ // (returns 0) because that stream may be in the worker thread call to stop.
+ const bool mLockStreamManagerStop;
+
std::unique_ptr<ThreadPool> mThreadPool; // locked internally
// mStreamManagerLock is used to lock access for transitions between the
@@ -477,8 +485,6 @@ private:
// The paired stream may be active or restarting.
// No particular order.
std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock);
-
- const std::string mOpPackageName;
};
} // namespace android::soundpool
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 357cc63bd41e..a66d99fbd9f4 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -34,7 +34,8 @@ static struct fields_t {
jclass mSoundPoolClass;
} fields;
static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
- return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext));
}
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
struct audio_attributes_fields_t {
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 779318008a9b..0476216dcbed 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -314,6 +314,11 @@ Result DvrClient::flush() {
}
Result DvrClient::close() {
+ if (mDvrMQEventFlag != NULL) {
+ EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+ }
+ mDvrMQ = NULL;
+
if (mTunerDvr != NULL) {
Status s = mTunerDvr->close();
mTunerDvr = NULL;
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index f31d4651350a..8846e4d62522 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -259,6 +259,11 @@ Result FilterClient::setDataSource(sp<FilterClient> filterClient){
}
Result FilterClient::close() {
+ if (mFilterMQEventFlag != NULL) {
+ EventFlag::deleteEventFlag(&mFilterMQEventFlag);
+ }
+ mFilterMQ = NULL;
+
if (mTunerFilter != NULL) {
Status s = mTunerFilter->close();
closeAvSharedMemory();
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 6fab9e4641b6..5afe0b5d64bc 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -50,7 +50,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.TrafficStatsConstants;
+import com.android.net.module.util.NetworkStackConstants;
import java.io.IOException;
import java.lang.reflect.Field;
@@ -239,7 +239,7 @@ public class CaptivePortalLoginActivity extends Activity {
HttpURLConnection urlConnection = null;
int httpResponseCode = 500;
int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_PROBE);
+ NetworkStackConstants.TAG_SYSTEM_PROBE);
try {
urlConnection = (HttpURLConnection) mNetwork.openConnection(
new URL(mCm.getCaptivePortalServerUrl()));
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index cdf4851a3e3d..36e59ba832cc 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; bestuur te word"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Stel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Stel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index a03ea0dfcd4c..0fefa8af15de 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"በ&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; እንዲያስተዳድር ያቀናብሩት"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string>
- <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; ለማስተዳደር"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 970c46bd6133..ca3b9f364c76 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‏اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏اضبط &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"يجب توفّر تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"نعم"</string>
- <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"‏ضبط &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; لإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
+ <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 477844c2a477..34ce062e0f4a 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ছেট কৰক - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হয়"</string>
- <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index f10c639a0368..8e4a202b18ea 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tərəfindən idarə olunmasını ayarlayın - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME">%1$s</xliff:g> tələb olunur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string>
- <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etmək üçün &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ayarlayın"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
+ <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index e8542f364027..ef19c48207f3 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Podesite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tako da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 13be6f245ac0..4366a0895222 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
<string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3bda5e6104c8..77f3413f8b88 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Задайте &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"За управление на <xliff:g id="PROFILE_NAME">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Задайте &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index d3bc5152af9a..5fa47813dfe2 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করবে"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
<string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; সেট করুন"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"না থাক"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; সেট করুন"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
+ <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 905b3061ffc6..18153b589372 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Potrebna je aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 86dc6940abe2..bbd1125eef6d 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
<string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defineix que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> és necessària per gestionar el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Defineix que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el dispositiu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 389ccd0ccc95..c5fed3372030 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ke správě profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ano"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, díky"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Nastavit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pro správu vašeho zařízení: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 5a31f9bba4b6..21280d527b43 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angiv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tak"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Indstil &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til at administrere &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index b643eb2936ce..1c7ec74f4494 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) zu verwalten. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 60de2ffe0572..162d6fc8b32d 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string>
- <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Ορίστε την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; για διαχείριση της συσκευής &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index 2fed1ae7fec3..de89b39a37dd 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index 2fed1ae7fec3..de89b39a37dd 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index 2fed1ae7fec3..de89b39a37dd 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index 2fed1ae7fec3..de89b39a37dd 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index f3c4b1dcb434..10c20121883b 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -20,8 +20,9 @@
<string name="chooser_title" msgid="2262294130493605839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
<string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎Set &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ - &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
- <string name="profile_summary" msgid="2009764182871566255">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="consent_yes" msgid="4055438216605487056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎Yes‎‏‎‎‏‎"</string>
- <string name="consent_no" msgid="1335543792857823917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎No thanks‎‏‎‎‏‎"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <string name="profile_summary" msgid="2059360676631420073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎This app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
+ <string name="consent_no" msgid="2640796915611404382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎Don’t allow‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 4fbb57ed9440..8dae51c706b8 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; lo administre"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se requiere <xliff:g id="APP_NAME">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para administrar el dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 5ca9305ce4d6..a307b61a44f9 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Haz que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se necesita <xliff:g id="APP_NAME">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para que gestione tu dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 357f05237b85..2fb2bc49daff 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
<string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME">%2$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jah"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Määrake rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; haldama teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 14c7154acdd3..646f844ac572 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kudea dezan"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kudeatzeko. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bai"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ez"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Konfiguratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudea dezan"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da aplikazioa. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 6bb9620f09f7..69b9647a07dd 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‏انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>‏&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏تنظیم &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‏&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> به <xliff:g id="APP_NAME">%1$s</xliff:g> نیاز دارید. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"بله"</string>
- <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"‏تنظیم &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"مجاز"</string>
+ <string name="consent_no" msgid="2640796915611404382">"مجاز نیست"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 5a9c1cd66aa8..b174b8a9d5bb 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
<string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Aseta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnointiin. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Valitse &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; laitteen (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;) ylläpitäjäksi"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index b31babda1a00..696598d9f4b9 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Utiliser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non merci"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Utiliser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 08c93a2c31c6..787794bf3893 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Définir &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, merci"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Définir &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index c95b90e73edc..a3efc4c0610d 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Necesítase a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Si"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para que xestione o teu dispositivo &lt;strong&gt;(<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 7e4104208446..1b0fe1ac2efc 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સેટ કરો"</string>
- <string name="profile_summary" msgid="2009764182871566255">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
- <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"તમારું &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; મેનેજ કરવા માટે, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સેટ કરો"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
+ <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index ac95cc620bfa..dee30f259562 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; की मदद से प्रबंधित किया जा सके"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; को प्रबंधित करने के लिए, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को सेट करें"</string>
- <string name="profile_summary" msgid="2009764182871566255">"आपके <xliff:g id="PROFILE_NAME">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"हां"</string>
- <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; को मैनेज करने के लिए, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट अप करें"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"यह ऐप्लिकेशन, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> मैनेज करने के लिए ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index df8451f5f127..bc36521dd1e1 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> potrebna je za upravljanje vašim <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index ff1c6c54d7ba..ef505443ffee 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás beállítása a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kezelésére"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Szükség van a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kezeléséhez. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Igen"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nem"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás beállítva a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> (&lt;strong&gt;&lt;/strong&gt;) kezelésére"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 194223d1d416..103361aa9cfa 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; հավելվածի կողմից"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ձեր <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Այո"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Ընտրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածը որպես &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքի կառավարիչ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է ձեր <xliff:g id="PROFILE_NAME">%1$s</xliff:g> պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 58bf3cb4bca4..225b276a608d 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Perlu <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index cc5b98939b71..7855a2ac4240 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
<string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Já"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 4cbefd801187..9e503e16e71d 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"È richiesta l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sì"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, grazie"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Configura l\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; affinché gestisca &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 8663e56ecf14..d293fb0f6c18 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‏בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
<string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
- <string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
+ <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index ca17336bfb23..ac8e2d2d0698 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; を管理するよう設定する"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME">%1$s</xliff:g> が必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"はい"</string>
- <string name="consent_no" msgid="1335543792857823917">"いいえ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; を管理する &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; の設定"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"このアプリは<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"許可"</string>
+ <string name="consent_no" msgid="2640796915611404382">"許可しない"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 300c94f63cbe..8b7680ee34c1 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-მა"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
<string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string>
- <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"დააყენეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, რათა მართოთ თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
+ <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 94d6c3ed2d5f..1ad854e3087b 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Иә"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беріңіз"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index db13fe7884af..7231c2d577a8 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string>
- <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"កំណត់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ដើម្បីគ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
+ <string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 0225166849fd..6f7532877fb5 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
- <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್‌ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 1363e57e39a3..5b171eaded63 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
<string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)을(를) 관리하도록 설정"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> 프로필을 관리하려면 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"예"</string>
- <string name="consent_no" msgid="1335543792857823917">"취소"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 앱이 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 설정"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"허용"</string>
+ <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index c01e2350aa04..f7c896be7f98 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; тарабынан башкарылсын"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
<string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарсын"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME">%1$s</xliff:g> керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкаруу үчүн &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосун жөндөңүз"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 68218dd79c50..8ad881f63e8b 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"ຕັ້ງຄ່າ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 5fd8280affca..c40d4a75753f 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; (pasirinkite)"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
<string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; būtų valdomas programos &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Norint valdyti jūsų <xliff:g id="PROFILE_NAME">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Taip"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; tvarkymo naudojant &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; nustatymas"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index bf036ec70d73..c842ee1436e3 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
<string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) pārvaldībai"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jā"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; iestatīšana ierīces &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; pārvaldībai"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 427ca8f940a0..fdc366e1900f 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, фала"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Поставете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; за да управувате со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index a48c45f73ae7..ae445a3f0f12 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
<string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; സജ്ജീകരിക്കുക - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string>
- <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യുന്നതിന് &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; സജ്ജീകരിക്കുക"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
+ <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 7ac20e613185..01850e707fb4 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
<string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г тохируулна уу - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Таны <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string>
- <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Та &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г тохируулна уу"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 144698b9bc4e..7eea9bf0941a 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
<string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
- <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 7bea2c91fd2d..e43a27f0cecf 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
<string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; anda"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk menguruskan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 9c2783cdcbbb..1bd3a1d9f9e7 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
<string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သတ်မှတ်ပါ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သတ်မှတ်ပါ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 26fbb0350edc..e4d247b4ae00 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei takk"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Velg at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; skal administrere &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index f289b3780672..45bfb5f469ee 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
<string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्न &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; तोक्नुहोस्"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"अँ"</string>
- <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 0c9cdffd4e17..9dc23e7ee750 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; instellen om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te beheren"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Je hebt <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, bedankt"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; instellen om je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index c8c680f8f3a9..4cfe0573dd42 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 0da94105a576..03693dc74274 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index b07af57a936a..3dbd2f75a68d 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ustaw aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> na urządzeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Tak"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, dziękuję"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Skonfiguruj aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby zarządzała urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 16906f62f9f1..91a9fa4a58b6 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Configure o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerenciar seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 745d1630dd86..5ad9389f82e2 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Não, obrigado"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Defina a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerir o seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 16906f62f9f1..91a9fa4a58b6 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Configure o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerenciar seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 187cfbdfe6f0..15f5393e7c75 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Setați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pentru a vă gestiona dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 8dd9a392712b..2f79416ffc4b 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Нет"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index 9e7c02e0c0d9..d108a25ce2da 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට <xliff:g id="APP_NAME">%1$s</xliff:g> අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string>
- <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට සකසන්න"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 55a47c2df427..d53728b34d5a 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Na správu profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Áno"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Nastavte aplikáciu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby spravovala zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 159afd543609..28492105f8f2 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Za upravljanje naprave <xliff:g id="PROFILE_NAME">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Nastavitev aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; za upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 6fa759c15905..a57835a1d92e 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Nevojitet <xliff:g id="APP_NAME">%1$s</xliff:g> për të menaxhuar profilin tënd të <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
- <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index fdbbe8e668f5..0b01cbce9a12 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Подесите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; тако да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index bfd25162aec6..f6dcec9054c7 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfigurera &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tack"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Konfigurera &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; för att hantera din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 437ae7f332e8..0999c5b87635 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Weka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hapana"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Weka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ili udhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 9b4a720a4863..884d57f5b61b 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
<string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அமையுங்கள்"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string>
- <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; சாதனத்தை நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அமையுங்கள்"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
+ <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 6e785de583aa..9c3e334c9373 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
<string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను సెటప్ చేయండి"</string>
- <string name="profile_summary" msgid="2009764182871566255">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"అవును"</string>
- <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index b727d42035dd..58ebda67e685 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
<string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ต้องใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g> <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string>
- <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"ตั้งค่าให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index a93282a8fae0..1a8328464b3e 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Kailangan ang <xliff:g id="APP_NAME">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Itakda ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 3abe064d60bc..b89e8748e04e 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazınızı yönetecek şekilde ayarlayın"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Evet"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetmek için &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını ayarlayın"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
+ <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 161d95e127e8..2ae852cb7c3f 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
<string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, щоб керувати своїм пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Налаштуйте додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, щоб керувати пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index dce18152dff4..83e20d8940cd 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو سیٹ کریں - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"آپ کے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
- <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 2ca27b530651..5681118c6998 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
<string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmalarini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> zarur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
- <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 06a1ab6846ae..3bbdb57a227c 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; quản lý"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
<string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Đặt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Cần có <xliff:g id="APP_NAME">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Có"</string>
- <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Đặt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; để quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 12bfcf3629cd..788f8f9edce1 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -20,8 +20,12 @@
<string name="chooser_title" msgid="2262294130493605839">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"设为由&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"若要管理<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"好"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了"</string>
+ <!-- no translation found for confirmation_title (814973816731238955) -->
+ <skip />
+ <!-- no translation found for profile_summary (2059360676631420073) -->
+ <skip />
+ <!-- no translation found for consent_yes (8344487259618762872) -->
+ <skip />
+ <!-- no translation found for consent_no (2640796915611404382) -->
+ <skip />
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 0c583b211035..be29925b72e2 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"選擇由 &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"設定 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"必須使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"設定 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 來管理您的 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 519f0e8a7082..80448690cec9 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"如要管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"你必須使用這個應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 7721b54166f5..3ed177cd6e08 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
<string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setha i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string>
+ <string name="confirmation_title" msgid="814973816731238955">"Setha i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuze iphathe i-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> yakho - &lt;strong&gt;&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 4dd012a8d074..44748e9cc692 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -29,7 +29,7 @@
<string name="profile_name_watch">watch</string>
<!-- Title of the device association confirmation dialog. -->
- <string name="confirmation_title">Set &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
+ <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
<!-- Text of the device profile permissions explanation in the association dialog. -->
<string name="profile_summary">This app is needed to manage your <xliff:g id="profile_name" example="watch">%1$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%2$s</xliff:g></string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index f1a98adc0ab2..91a6749ac42d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -128,9 +128,11 @@ public class CompanionDeviceActivity extends Activity {
if (useDeviceProfile) {
profileSummary.setVisibility(View.VISIBLE);
+ String deviceRef = getRequest().isSingleDevice()
+ ? getService().mDevicesFound.get(0).getDisplayName()
+ : profileName;
profileSummary.setText(getString(R.string.profile_summary,
- getCallingAppName(),
- profileName,
+ deviceRef,
profilePrivacyDisclaimer));
} else {
profileSummary.setVisibility(View.GONE);
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 86b85e8398ce..017ff51f366d 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -23,6 +23,25 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
+java_library {
+ name: "framework-connectivity-protos",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ // TODO: consider moving relevant .proto files directly to the module directory
+ ":framework-javastream-protos",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
+ jarjar_rules: "jarjar-rules-proto.txt",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
filegroup {
name: "framework-connectivity-internal-sources",
srcs: [
@@ -83,3 +102,39 @@ java_sdk_library {
],
permitted_packages: ["android.net", "com.android.connectivity.aidl"],
}
+
+java_library {
+ name: "framework-connectivity.impl",
+ // Instead of building against private API (framework.jar),
+ // build against core_platform + framework-minus-apex + module
+ // stub libs. This allows framework.jar to depend on this library,
+ // so it can be part of the private API until all clients have been migrated.
+ // TODO: just build against module_api, and remove this jar from
+ // the private API.
+ sdk_version: "core_platform",
+ srcs: [
+ ":framework-connectivity-sources",
+ ],
+ aidl: {
+ include_dirs: [
+ "frameworks/base/core/java", // For framework parcelables
+ "frameworks/native/aidl/binder", // For PersistableBundle.aidl
+ ],
+ },
+ libs: [
+ "framework-minus-apex",
+ // TODO: just framework-tethering, framework-wifi when building against module_api
+ "framework-tethering.stubs.module_lib",
+ "framework-wifi.stubs.module_lib",
+ "unsupportedappusage",
+ "ServiceConnectivityResources",
+ ],
+ static_libs: [
+ "framework-connectivity-protos",
+ "net-utils-device-common",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+ apex_available: ["com.android.tethering"],
+ installable: true,
+ permitted_packages: ["android.net", "com.android.connectivity.aidl"],
+}
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index a8f1a4d2a7f8..f22d4b7b779a 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -87,6 +87,7 @@ package android.net {
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method @Deprecated public static boolean isNetworkTypeValid(int);
+ method public void registerBestMatchingNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
@@ -143,6 +144,7 @@ package android.net {
public static class ConnectivityManager.NetworkCallback {
ctor public ConnectivityManager.NetworkCallback();
+ ctor public ConnectivityManager.NetworkCallback(int);
method public void onAvailable(@NonNull android.net.Network);
method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
@@ -150,6 +152,7 @@ package android.net {
method public void onLosing(@NonNull android.net.Network, int);
method public void onLost(@NonNull android.net.Network);
method public void onUnavailable();
+ field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1
}
public static interface ConnectivityManager.OnNetworkActiveListener {
@@ -293,6 +296,7 @@ package android.net {
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
method public int getOwnerUid();
method public int getSignalStrength();
+ method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
method @Nullable public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
@@ -399,6 +403,11 @@ package android.net {
method public android.net.NetworkRequest.Builder removeTransportType(int);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
+ public class ParseException extends java.lang.RuntimeException {
+ field public String response;
}
public class ProxyInfo implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 6df57c132382..bb296476c72f 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -7,9 +7,15 @@ package android.net {
public class ConnectivityManager {
method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
+ method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
+ method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
- method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+ field public static final String PRIVATE_DNS_MODE_OFF = "off";
+ field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+ field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
}
public final class NetworkAgentConfig implements android.os.Parcelable {
@@ -24,6 +30,11 @@ package android.net {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
+ public class ParseException extends java.lang.RuntimeException {
+ ctor public ParseException(@NonNull String);
+ ctor public ParseException(@NonNull String, @NonNull Throwable);
+ }
+
public final class TcpRepairWindow {
ctor public TcpRepairWindow(int, int, int, int, int, int);
field public final int maxWindow;
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index a732430e6a9c..4dca411cca24 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -56,7 +56,7 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
- method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -67,6 +67,8 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+ field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
+ field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
@@ -78,10 +80,6 @@ package android.net {
field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
}
- public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
- method public void onComplete();
- }
-
@Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
method @Deprecated public void onTetheringFailed();
@@ -296,6 +294,7 @@ package android.net {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+ method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
}
diff --git a/packages/Connectivity/framework/jarjar-rules-proto.txt b/packages/Connectivity/framework/jarjar-rules-proto.txt
new file mode 100644
index 000000000000..37b4dec1c331
--- /dev/null
+++ b/packages/Connectivity/framework/jarjar-rules-proto.txt
@@ -0,0 +1,3 @@
+keep android.net.NetworkCapabilitiesProto
+keep android.net.NetworkProto
+keep android.net.NetworkRequestProto
diff --git a/packages/Connectivity/framework/jarjar-rules.txt b/packages/Connectivity/framework/jarjar-rules.txt
new file mode 100644
index 000000000000..0959840f2dd2
--- /dev/null
+++ b/packages/Connectivity/framework/jarjar-rules.txt
@@ -0,0 +1,10 @@
+rule com.android.net.module.util.** android.net.connectivity.framework.util.@1
+
+# TODO (b/149403767): remove the annotations from net-utils-device-common instead of here
+zap android.annotation.**
+zap com.android.net.module.annotation.**
+zap com.android.internal.annotations.**
+
+rule android.net.NetworkCapabilitiesProto* android.net.connectivity.proto.NetworkCapabilitiesProto@1
+rule android.net.NetworkProto* android.net.connectivity.proto.NetworkProto@1
+rule android.net.NetworkRequestProto* android.net.connectivity.proto.NetworkRequestProto@1
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index bbf45596e789..ba5eb1090dbf 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -16,13 +16,15 @@
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.LISTEN;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
import static android.net.QosCallback.QosCallbackRegistrationException;
+import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -31,11 +33,13 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -43,6 +47,7 @@ import android.net.SocketKeepalive.Callback;
import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringRequest;
+import android.net.wifi.WifiNetworkSuggestion;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -59,9 +64,11 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Range;
@@ -70,7 +77,6 @@ import android.util.SparseIntArray;
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.Protocol;
import libcore.net.event.NetworkEventDispatcher;
@@ -801,24 +807,27 @@ public class ConnectivityManager {
/**
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final String PRIVATE_DNS_MODE_OFF = "off";
/**
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
/**
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
- /**
- * The default Private DNS mode.
- *
- * This may change from release to release or may become dependent upon
- * the capabilities of the underlying platform.
- *
- * @hide
- */
- public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(value = {
+ PRIVATE_DNS_MODE_OFF,
+ PRIVATE_DNS_MODE_OPPORTUNISTIC,
+ PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+ })
+ public @interface PrivateDnsMode {}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
@@ -962,6 +971,33 @@ public class ConnectivityManager {
}
/**
+ * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+ * Specify that the traffic for this user should by follow the default rules.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
+
+ /**
+ * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+ * Specify that the traffic for this user should by default go on a network with
+ * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
+ * if no such network is available.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ PROFILE_NETWORK_PREFERENCE_DEFAULT,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE
+ })
+ public @interface ProfileNetworkPreference {
+ }
+
+ /**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
*
@@ -1314,7 +1350,7 @@ public class ConnectivityManager {
}
/**
- * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
+ * Returns an array of {@link NetworkCapabilities} objects, representing
* the Networks that applications run by the given user will use by default.
* @hide
*/
@@ -1394,11 +1430,19 @@ public class ConnectivityManager {
}
/**
- * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
+ * Get the {@link NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
*
+ * This will remove any location sensitive data in {@link TransportInfo} embedded in
+ * {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like
+ * {@link android.net.wifi.WifiInfo} contain location sensitive information. Retrieving
+ * this location sensitive information (subject to app's location permissions) will be
+ * noted by system. To include any location sensitive data in {@link TransportInfo},
+ * use a {@link NetworkCallback} with
+ * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} flag.
+ *
* @param network The {@link Network} object identifying the network in question.
- * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
+ * @return The {@link NetworkCapabilities} for the network, or {@code null}.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@Nullable
@@ -1996,7 +2040,7 @@ public class ConnectivityManager {
dup = createInvalidFd();
}
return new NattSocketKeepalive(mService, network, dup,
- INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
+ -1 /* Unused */, source, destination, executor, callback);
}
/**
@@ -3196,10 +3240,6 @@ public class ConnectivityManager {
}
}
- // TODO : remove this method. It is a stopgap measure to help sheperding a number
- // of dependent changes that would conflict throughout the automerger graph. Having this
- // temporarily helps with the process of going through with all these dependent changes across
- // the entire tree.
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
@@ -3209,20 +3249,8 @@ public class ConnectivityManager {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_FACTORY})
public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkAgentConfig config) {
- return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
- }
-
- /**
- * @hide
- * Register a NetworkAgent with ConnectivityService.
- * @return Network corresponding to NetworkAgent.
- */
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+ NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config,
+ int providerId) {
try {
return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId);
} catch (RemoteException e) {
@@ -3244,6 +3272,54 @@ public class ConnectivityManager {
*/
public static class NetworkCallback {
/**
+ * No flags associated with this callback.
+ * @hide
+ */
+ public static final int FLAG_NONE = 0;
+ /**
+ * Use this flag to include any location sensitive data in {@link NetworkCapabilities} sent
+ * via {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}.
+ * <p>
+ * These include:
+ * <li> Some transport info instances (retrieved via
+ * {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
+ * contain location sensitive information.
+ * <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
+ * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+ * </p>
+ * <p>
+ * Note:
+ * <li> Retrieving this location sensitive information (subject to app's location
+ * permissions) will be noted by system. </li>
+ * <li> Without this flag any {@link NetworkCapabilities} provided via the callback does
+ * not include location sensitive info.
+ * </p>
+ */
+ public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = "FLAG_", value = {
+ FLAG_NONE,
+ FLAG_INCLUDE_LOCATION_INFO
+ })
+ public @interface Flag { }
+
+ /**
+ * All the valid flags for error checking.
+ */
+ private static final int VALID_FLAGS = FLAG_INCLUDE_LOCATION_INFO;
+
+ public NetworkCallback() {
+ this(FLAG_NONE);
+ }
+
+ public NetworkCallback(@Flag int flags) {
+ Preconditions.checkArgument((flags & VALID_FLAGS) == flags);
+ mFlags = flags;
+ }
+
+ /**
* Called when the framework connects to a new network to evaluate whether it satisfies this
* request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
* callback. There is no guarantee that this new network will satisfy any requests, or that
@@ -3380,7 +3456,7 @@ public class ConnectivityManager {
* calling these methods while in a callback may return an outdated or even a null object.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+ * @param networkCapabilities The new {@link NetworkCapabilities} for this
* network.
*/
public void onCapabilitiesChanged(@NonNull Network network,
@@ -3449,6 +3525,7 @@ public class ConnectivityManager {
public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
private NetworkRequest networkRequest;
+ private final int mFlags;
}
/**
@@ -3473,29 +3550,28 @@ public class ConnectivityManager {
}
}
- private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
/** @hide */
- public static final int CALLBACK_PRECHECK = BASE + 1;
+ public static final int CALLBACK_PRECHECK = 1;
/** @hide */
- public static final int CALLBACK_AVAILABLE = BASE + 2;
+ public static final int CALLBACK_AVAILABLE = 2;
/** @hide arg1 = TTL */
- public static final int CALLBACK_LOSING = BASE + 3;
+ public static final int CALLBACK_LOSING = 3;
/** @hide */
- public static final int CALLBACK_LOST = BASE + 4;
+ public static final int CALLBACK_LOST = 4;
/** @hide */
- public static final int CALLBACK_UNAVAIL = BASE + 5;
+ public static final int CALLBACK_UNAVAIL = 5;
/** @hide */
- public static final int CALLBACK_CAP_CHANGED = BASE + 6;
+ public static final int CALLBACK_CAP_CHANGED = 6;
/** @hide */
- public static final int CALLBACK_IP_CHANGED = BASE + 7;
+ public static final int CALLBACK_IP_CHANGED = 7;
/** @hide obj = NetworkCapabilities, arg1 = seq number */
- private static final int EXPIRE_LEGACY_REQUEST = BASE + 8;
+ private static final int EXPIRE_LEGACY_REQUEST = 8;
/** @hide */
- public static final int CALLBACK_SUSPENDED = BASE + 9;
+ public static final int CALLBACK_SUSPENDED = 9;
/** @hide */
- public static final int CALLBACK_RESUMED = BASE + 10;
+ public static final int CALLBACK_RESUMED = 10;
/** @hide */
- public static final int CALLBACK_BLK_CHANGED = BASE + 11;
+ public static final int CALLBACK_BLK_CHANGED = 11;
/** @hide */
public static String getCallbackName(int whichCallback) {
@@ -3638,14 +3714,15 @@ public class ConnectivityManager {
}
Messenger messenger = new Messenger(handler);
Binder binder = new Binder();
+ final int callbackFlags = callback.mFlags;
if (reqType == LISTEN) {
request = mService.listenForNetwork(
- need, messenger, binder, callingPackageName,
+ need, messenger, binder, callbackFlags, callingPackageName,
getAttributionTag());
} else {
request = mService.requestNetwork(
need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
- callingPackageName, getAttributionTag());
+ callbackFlags, callingPackageName, getAttributionTag());
}
if (request != null) {
sCallbacks.put(request, callback);
@@ -3692,7 +3769,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link NetworkCapabilities}.
*
* <p>This method will attempt to find the best network that matches the passed
* {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
@@ -3776,7 +3853,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link NetworkCapabilities}.
*
* This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
* but runs all the callbacks on the passed Handler.
@@ -3798,7 +3875,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+ * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
* by a timeout.
*
* This function behaves identically to the non-timed-out version
@@ -3833,7 +3910,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+ * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
* by a timeout.
*
* This method behaves identically to
@@ -3878,7 +3955,7 @@ public class ConnectivityManager {
/**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link NetworkCapabilities}.
*
* This function behaves identically to the version that takes a NetworkCallback, but instead
* of {@link NetworkCallback} a {@link PendingIntent} is used. This means
@@ -4190,6 +4267,36 @@ public class ConnectivityManager {
}
/**
+ * Registers to receive notifications about the best matching network which satisfy the given
+ * {@link NetworkRequest}. The callbacks will continue to be called until
+ * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+ * called.
+ *
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * {@link #registerNetworkCallback} and its variants and {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+ * networks change state.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ */
+ @SuppressLint("ExecutorRegistration")
+ public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+ final NetworkCapabilities nc = request.networkCapabilities;
+ final CallbackHandler cbHandler = new CallbackHandler(handler);
+ sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler);
+ }
+
+ /**
* Requests bandwidth update for a given {@link Network} and returns whether the update request
* is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
* network connection for updated bandwidth information. The caller will be notified via
@@ -4898,7 +5005,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+ * Request a network to satisfy a set of {@link NetworkCapabilities}, but
* does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
* be used to request that the system provide a network without causing the network to be
* in the foreground.
@@ -4979,23 +5086,10 @@ public class ConnectivityManager {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
})
public void requestBackgroundNetwork(@NonNull NetworkRequest request,
- @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+ @NonNull Handler handler, @NonNull NetworkCallback networkCallback) {
final NetworkCapabilities nc = request.networkCapabilities;
sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
- TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
- }
-
- /**
- * Listener for {@link #setOemNetworkPreference(OemNetworkPreferences, Executor,
- * OnSetOemNetworkPreferenceListener)}.
- * @hide
- */
- @SystemApi
- public interface OnSetOemNetworkPreferenceListener {
- /**
- * Called when setOemNetworkPreference() successfully completes.
- */
- void onComplete();
+ TYPE_NONE, new CallbackHandler(handler));
}
/**
@@ -5020,16 +5114,16 @@ public class ConnectivityManager {
@RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE)
public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference,
@Nullable @CallbackExecutor final Executor executor,
- @Nullable final OnSetOemNetworkPreferenceListener listener) {
+ @Nullable final Runnable listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
if (null != listener) {
Objects.requireNonNull(executor, "Executor must be non-null");
}
- final IOnSetOemNetworkPreferenceListener listenerInternal = listener == null ? null :
- new IOnSetOemNetworkPreferenceListener.Stub() {
+ final IOnCompleteListener listenerInternal = listener == null ? null :
+ new IOnCompleteListener.Stub() {
@Override
public void onComplete() {
- executor.execute(listener::onComplete);
+ executor.execute(listener::run);
}
};
@@ -5040,4 +5134,88 @@ public class ConnectivityManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Request that a user profile is put by default on a network matching a given preference.
+ *
+ * See the documentation for the individual preferences for a description of the supported
+ * behaviors.
+ *
+ * @param profile the profile concerned.
+ * @param preference the preference for this profile.
+ * @param executor an executor to execute the listener on. Optional if listener is null.
+ * @param listener an optional listener to listen for completion of the operation.
+ * @throws IllegalArgumentException if {@code profile} is not a valid user profile.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @hide
+ */
+ // This function is for establishing per-profile default networking and can only be called by
+ // the device policy manager, running as the system server. It would make no sense to call it
+ // on a context for a user because it does not establish a setting on behalf of a user, rather
+ // it establishes a setting for a user on behalf of the DPM.
+ @SuppressLint({"UserHandle"})
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+ @ProfileNetworkPreference final int preference,
+ @Nullable @CallbackExecutor final Executor executor,
+ @Nullable final Runnable listener) {
+ if (null != listener) {
+ Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
+ }
+ final IOnCompleteListener proxy;
+ if (null == listener) {
+ proxy = null;
+ } else {
+ proxy = new IOnCompleteListener.Stub() {
+ @Override
+ public void onComplete() {
+ executor.execute(listener::run);
+ }
+ };
+ }
+ try {
+ mService.setProfileNetworkPreference(profile, preference, proxy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ // The first network ID of IPSec tunnel interface.
+ private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512
+ // The network ID range of IPSec tunnel interface.
+ private static final int TUN_INTF_NETID_RANGE = 0x0400; // 0x0400 = 1024
+
+ /**
+ * Get the network ID range reserved for IPSec tunnel interfaces.
+ *
+ * @return A Range which indicates the network ID range of IPSec tunnel interface.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @NonNull
+ public static Range<Integer> getIpSecNetIdRange() {
+ return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
+ }
+
+ /**
+ * Get private DNS mode from settings.
+ *
+ * @param context The Context to query the private DNS mode from settings.
+ * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @NonNull
+ @PrivateDnsMode
+ public static String getPrivateDnsMode(@NonNull Context context) {
+ final ContentResolver cr = context.getContentResolver();
+ String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
+ if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
+ // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
+ // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
+ if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+ return mode;
+ }
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityResources.java b/packages/Connectivity/framework/src/android/net/ConnectivityResources.java
new file mode 100644
index 000000000000..18f0de0cc9a1
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityResources.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Utility to obtain the {@link com.android.server.ConnectivityService} {@link Resources}, in the
+ * ServiceConnectivityResources APK.
+ * @hide
+ */
+public class ConnectivityResources {
+ private static final String RESOURCES_APK_INTENT =
+ "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK";
+ private static final String RES_PKG_DIR = "/apex/com.android.tethering/";
+
+ @NonNull
+ private final Context mContext;
+
+ @Nullable
+ private Context mResourcesContext = null;
+
+ @Nullable
+ private static Context sTestResourcesContext = null;
+
+ public ConnectivityResources(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Convenience method to mock all resources for the duration of a test.
+ *
+ * Call with a null context to reset after the test.
+ */
+ @VisibleForTesting
+ public static void setResourcesContextForTest(@Nullable Context testContext) {
+ sTestResourcesContext = testContext;
+ }
+
+ /**
+ * Get the {@link Context} of the resources package.
+ */
+ public synchronized Context getResourcesContext() {
+ if (sTestResourcesContext != null) {
+ return sTestResourcesContext;
+ }
+
+ if (mResourcesContext != null) {
+ return mResourcesContext;
+ }
+
+ final List<ResolveInfo> pkgs = mContext.getPackageManager()
+ .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY);
+ pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR));
+ if (pkgs.size() > 1) {
+ Log.wtf(ConnectivityResources.class.getSimpleName(),
+ "More than one package found: " + pkgs);
+ }
+ if (pkgs.isEmpty()) {
+ throw new IllegalStateException("No connectivity resource package found");
+ }
+
+ final Context pkgContext;
+ try {
+ pkgContext = mContext.createPackageContext(
+ pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("Resolved package not found", e);
+ }
+
+ mResourcesContext = pkgContext;
+ return pkgContext;
+ }
+
+ /**
+ * Get the {@link Resources} of the ServiceConnectivityResources APK.
+ */
+ public Resources get() {
+ return getResourcesContext().getResources();
+ }
+}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
new file mode 100644
index 000000000000..d4543654522f
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * A manager class for connectivity module settings.
+ *
+ * @hide
+ */
+public class ConnectivitySettingsManager {
+
+ private ConnectivitySettingsManager() {}
+
+ /**
+ * Whether to automatically switch away from wifi networks that lose Internet access.
+ * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
+ * avoids such networks. Valid values are:
+ *
+ * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+ * null: Ask the user whether to switch away from bad wifi.
+ * 1: Avoid bad wifi.
+ */
+ public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+
+ /**
+ * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
+ * overridden by the system based on device or application state. If null, the value
+ * specified by config_networkMeteredMultipathPreference is used.
+ */
+ public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
+ "network_metered_multipath_preference";
+}
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index cd49258d1c47..d83cc163b53f 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -20,7 +20,7 @@ import android.app.PendingIntent;
import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager;
import android.net.IConnectivityDiagnosticsCallback;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
import android.net.INetworkActivityListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
@@ -30,6 +30,7 @@ import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.NetworkScore;
import android.net.NetworkState;
import android.net.NetworkStateSnapshot;
import android.net.OemNetworkPreferences;
@@ -42,6 +43,7 @@ import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import com.android.connectivity.aidl.INetworkAgent;
@@ -138,12 +140,12 @@ interface IConnectivityManager
void declareNetworkRequestUnfulfillable(in NetworkRequest request);
Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
- in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+ in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config,
in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
- String callingPackageName, String callingAttributionTag);
+ int callbackFlags, String callingPackageName, String callingAttributionTag);
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation, String callingPackageName, String callingAttributionTag);
@@ -151,7 +153,7 @@ interface IConnectivityManager
void releasePendingNetworkRequest(in PendingIntent operation);
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, in IBinder binder, String callingPackageName,
+ in Messenger messenger, in IBinder binder, int callbackFlags, String callingPackageName,
String callingAttributionTag);
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
@@ -214,5 +216,8 @@ interface IConnectivityManager
void unregisterQosCallback(in IQosCallback callback);
void setOemNetworkPreference(in OemNetworkPreferences preference,
- in IOnSetOemNetworkPreferenceListener listener);
+ in IOnCompleteListener listener);
+
+ void setProfileNetworkPreference(in UserHandle profile, int preference,
+ in IOnCompleteListener listener);
}
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
index d2ee7d13b05f..bf4481afc53d 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -113,7 +113,7 @@ public final class IpPrefix implements Parcelable {
// first statement in constructor". We could factor out setting the member variables to an
// init() method, but if we did, then we'd have to make the members non-final, or "error:
// cannot assign a value to final variable address". So we just duplicate the code here.
- Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(prefix);
this.address = ipAndMask.first.getAddress();
this.prefixLength = ipAndMask.second;
checkAndMaskAddressAndPrefixLength();
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
index d1bdaa078cdc..d48b8c71f495 100644
--- a/packages/Connectivity/framework/src/android/net/LinkAddress.java
+++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java
@@ -325,7 +325,7 @@ public class LinkAddress implements Parcelable {
public LinkAddress(@NonNull String address, int flags, int scope) {
// This may throw an IllegalArgumentException; catching it is the caller's responsibility.
// TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
- Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(address);
init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
}
diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
index 46141e0d0c1e..7245db3b17db 100644
--- a/packages/Connectivity/framework/src/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -30,10 +30,10 @@ import android.system.OsConstants;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
-import com.android.okhttp.internalandroidapi.Dns;
-import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
import libcore.io.IoUtils;
+import libcore.net.http.Dns;
+import libcore.net.http.HttpURLConnectionFactory;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -299,7 +299,7 @@ public class Network implements Parcelable {
// Set configuration on the HttpURLConnectionFactory that will be good for all
// connections created by this Network. Configuration that might vary is left
// until openConnection() and passed as arguments.
- HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
+ HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance();
urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
// A private connection pool just for this Network.
urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 27aa15d1e1e4..a127c6f6de26 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -37,7 +37,6 @@ import android.util.Log;
import com.android.connectivity.aidl.INetworkAgent;
import com.android.connectivity.aidl.INetworkAgentRegistry;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -125,7 +124,10 @@ public abstract class NetworkAgent {
*/
public final int providerId;
- private static final int BASE = Protocol.BASE_NETWORK_AGENT;
+ // ConnectivityService parses message constants from itself and NetworkAgent with MessageUtils
+ // for debugging purposes, and crashes if some messages have the same values.
+ // TODO: have ConnectivityService store message names in different maps and remove this base
+ private static final int BASE = 200;
/**
* Sent by ConnectivityService to the NetworkAgent to inform it of
@@ -371,6 +373,14 @@ public abstract class NetworkAgent {
return ni;
}
+ // Temporary backward compatibility constructor
+ public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+ @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+ this(context, looper, logTag, nc, lp,
+ new NetworkScore.Builder().setLegacyInt(score).build(), config, provider);
+ }
+
/**
* Create a new network agent.
* @param context a {@link Context} to get system services from.
@@ -382,10 +392,12 @@ public abstract class NetworkAgent {
* @param score the initial score of this network. Update with sendNetworkScore.
* @param config an immutable {@link NetworkAgentConfig} for this agent.
* @param provider the {@link NetworkProvider} managing this agent.
+ * @hide TODO : unhide when impl is complete
*/
public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
- @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
- @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+ @NonNull NetworkScore score, @NonNull NetworkAgentConfig config,
+ @Nullable NetworkProvider provider) {
this(looper, context, logTag, nc, lp, score, config,
provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
getLegacyNetworkInfo(config));
@@ -395,12 +407,12 @@ public abstract class NetworkAgent {
public final Context context;
public final NetworkCapabilities capabilities;
public final LinkProperties properties;
- public final int score;
+ public final NetworkScore score;
public final NetworkAgentConfig config;
public final NetworkInfo info;
InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
- @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
- @NonNull NetworkInfo info) {
+ @NonNull LinkProperties properties, @NonNull NetworkScore score,
+ @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) {
this.context = context;
this.capabilities = capabilities;
this.properties = properties;
@@ -412,8 +424,9 @@ public abstract class NetworkAgent {
private volatile InitialConfiguration mInitialConfiguration;
private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
- @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
- @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) {
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+ @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId,
+ @NonNull NetworkInfo ni) {
mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
mNetworkInfo = new NetworkInfo(ni);
@@ -875,13 +888,22 @@ public abstract class NetworkAgent {
/**
* Must be called by the agent to update the score of this network.
*
+ * @param score the new score.
+ * @hide TODO : unhide when impl is complete
+ */
+ public final void sendNetworkScore(@NonNull NetworkScore score) {
+ Objects.requireNonNull(score);
+ queueOrSendMessage(reg -> reg.sendScore(score));
+ }
+
+ /**
+ * Must be called by the agent to update the score of this network.
+ *
* @param score the new score, between 0 and 99.
+ * deprecated use sendNetworkScore(NetworkScore) TODO : remove in S.
*/
public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
- if (score < 0) {
- throw new IllegalArgumentException("Score must be >= 0");
- }
- queueOrSendMessage(reg -> reg.sendScore(score));
+ sendNetworkScore(new NetworkScore.Builder().setLegacyInt(score).build());
}
/**
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c82cd3b4f357..058f3c999dd7 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -25,6 +25,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.wifi.WifiNetworkSuggestion;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -131,6 +132,7 @@ public final class NetworkCapabilities implements Parcelable {
mPrivateDnsBroken = false;
mRequestorUid = Process.INVALID_UID;
mRequestorPackageName = null;
+ mSubIds = new ArraySet<>();
}
/**
@@ -159,6 +161,7 @@ public final class NetworkCapabilities implements Parcelable {
mPrivateDnsBroken = nc.mPrivateDnsBroken;
mRequestorUid = nc.mRequestorUid;
mRequestorPackageName = nc.mRequestorPackageName;
+ mSubIds = new ArraySet<>(nc.mSubIds);
}
/**
@@ -1048,6 +1051,16 @@ public final class NetworkCapabilities implements Parcelable {
*
* Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
* this field cleared out.
+ *
+ * <p>
+ * This field will only be populated for VPN and wifi network suggestor apps (i.e using
+ * {@link WifiNetworkSuggestion}), and only for the network they own.
+ * In the case of wifi network suggestors apps, this field is also location sensitive, so the
+ * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
+ * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
+ * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
+ * callback. The app will be blamed for location access if this field is included.
+ * </p>
*/
public int getOwnerUid() {
return mOwnerUid;
@@ -1655,6 +1668,7 @@ public final class NetworkCapabilities implements Parcelable {
combineSSIDs(nc);
combineRequestor(nc);
combineAdministratorUids(nc);
+ combineSubIds(nc);
}
/**
@@ -1674,8 +1688,9 @@ public final class NetworkCapabilities implements Parcelable {
&& satisfiedBySpecifier(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc))
- && (onlyImmutable || satisfiedBySSID(nc)))
- && (onlyImmutable || satisfiedByRequestor(nc));
+ && (onlyImmutable || satisfiedBySSID(nc))
+ && (onlyImmutable || satisfiedByRequestor(nc))
+ && (onlyImmutable || satisfiedBySubIds(nc)));
}
/**
@@ -1771,7 +1786,8 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsOwnerUid(that)
&& equalsPrivateDnsBroken(that)
&& equalsRequestor(that)
- && equalsAdministratorUids(that);
+ && equalsAdministratorUids(that)
+ && equalsSubIds(that);
}
@Override
@@ -1793,7 +1809,8 @@ public final class NetworkCapabilities implements Parcelable {
+ Objects.hashCode(mPrivateDnsBroken) * 47
+ Objects.hashCode(mRequestorUid) * 53
+ Objects.hashCode(mRequestorPackageName) * 59
- + Arrays.hashCode(mAdministratorUids) * 61;
+ + Arrays.hashCode(mAdministratorUids) * 61
+ + Objects.hashCode(mSubIds) * 67;
}
@Override
@@ -1827,6 +1844,7 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeInt(mOwnerUid);
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
+ dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1850,6 +1868,11 @@ public final class NetworkCapabilities implements Parcelable {
netCap.mOwnerUid = in.readInt();
netCap.mRequestorUid = in.readInt();
netCap.mRequestorPackageName = in.readString();
+ netCap.mSubIds = new ArraySet<>();
+ final int[] subIdInts = Objects.requireNonNull(in.createIntArray());
+ for (int i = 0; i < subIdInts.length; i++) {
+ netCap.mSubIds.add(subIdInts[i]);
+ }
return netCap;
}
@Override
@@ -1933,11 +1956,14 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" SSID: ").append(mSSID);
}
-
if (mPrivateDnsBroken) {
sb.append(" PrivateDnsBroken");
}
+ if (!mSubIds.isEmpty()) {
+ sb.append(" SubscriptionIds: ").append(mSubIds);
+ }
+
sb.append("]");
return sb.toString();
}
@@ -2251,6 +2277,67 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
+ * Set of the subscription IDs that identifies the network or request, empty if none.
+ */
+ @NonNull
+ private ArraySet<Integer> mSubIds = new ArraySet<>();
+
+ /**
+ * Sets the subscription ID set that associated to this network or request.
+ *
+ * @hide
+ */
+ @NonNull
+ public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+ mSubIds = new ArraySet(Objects.requireNonNull(subIds));
+ return this;
+ }
+
+ /**
+ * Gets the subscription ID set that associated to this network or request.
+ * @return
+ */
+ @NonNull
+ public Set<Integer> getSubIds() {
+ return new ArraySet<>(mSubIds);
+ }
+
+ /**
+ * Tests if the subscription ID set of this network is the same as that of the passed one.
+ */
+ private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+ return Objects.equals(mSubIds, nc.mSubIds);
+ }
+
+ /**
+ * Check if the subscription ID set requirements of this object are matched by the passed one.
+ * If specified in the request, the passed one need to have at least one subId and at least
+ * one of them needs to be in the request set.
+ */
+ private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+ if (mSubIds.isEmpty()) return true;
+ if (nc.mSubIds.isEmpty()) return false;
+ for (final Integer subId : nc.mSubIds) {
+ if (mSubIds.contains(subId)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Combine subscription ID set of the capabilities.
+ *
+ * <p>This is only legal if the subscription Ids are equal.
+ *
+ * <p>If both subscription IDs are not equal, they belong to different subscription
+ * (or no subscription). In this case, it would not make sense to add them together.
+ */
+ private void combineSubIds(@NonNull NetworkCapabilities nc) {
+ if (!Objects.equals(mSubIds, nc.mSubIds)) {
+ throw new IllegalStateException("Can't combine two subscription ID sets");
+ }
+ }
+
+ /**
* Builder class for NetworkCapabilities.
*
* This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
@@ -2556,6 +2643,18 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
+ * Set the subscription ID set.
+ *
+ * @param subIds a set that represent the subscription IDs. Empty if clean up.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setSubIds(@NonNull final Set<Integer> subIds) {
+ mCaps.setSubIds(subIds);
+ return this;
+ }
+
+ /**
* Builds the instance of the capabilities.
*
* @return the built instance of NetworkCapabilities.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
index d752901e2eb0..bb2349459331 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkInfo.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -164,7 +163,7 @@ public class NetworkInfo implements Parcelable {
* @param typeName a human-readable string for the network type, or an empty string or null.
* @param subtypeName a human-readable string for the subtype, or an empty string or null.
*/
- public NetworkInfo(int type, @NetworkType int subtype,
+ public NetworkInfo(int type, int subtype,
@Nullable String typeName, @Nullable String subtypeName) {
if (!ConnectivityManager.isNetworkTypeValid(type)
&& type != ConnectivityManager.TYPE_NONE) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 17a8ee1720c4..dbe3ecc4d775 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -114,6 +114,10 @@ public class NetworkRequest implements Parcelable {
* for the network (if any) that satisfies the default Internet
* request.
*
+ * - TRACK_BEST, which causes the framework to send callbacks about
+ * the single, highest scoring current network (if any) that matches
+ * the specified NetworkCapabilities.
+ *
* - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
* to retain the NET_CAPABILITY_FOREGROUND capability. A network with
* no foreground requests is in the background. A network that has
@@ -136,6 +140,7 @@ public class NetworkRequest implements Parcelable {
REQUEST,
BACKGROUND_REQUEST,
TRACK_SYSTEM_DEFAULT,
+ LISTEN_FOR_BEST,
};
/**
@@ -456,6 +461,21 @@ public class NetworkRequest implements Parcelable {
}
nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
}
+
+ /**
+ * Sets the optional subscription ID set.
+ * <p>
+ * This specify the subscription IDs requirement.
+ * A network will satisfy this request only if it matches one of the subIds in this set.
+ * An empty set matches all networks, including those without a subId.
+ *
+ * @param subIds A {@code Set} that represents subscription IDs.
+ */
+ @NonNull
+ public Builder setSubIds(@NonNull Set<Integer> subIds) {
+ mNetworkCapabilities.setSubIds(subIds);
+ return this;
+ }
}
// implement the Parcelable interface
@@ -494,6 +514,15 @@ public class NetworkRequest implements Parcelable {
}
/**
+ * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST.
+ *
+ * @hide
+ */
+ public boolean isListenForBest() {
+ return type == Type.LISTEN_FOR_BEST;
+ }
+
+ /**
* Returns true iff. the contained NetworkRequest is one that:
*
* - should be associated with at most one satisfying network
diff --git a/packages/Connectivity/framework/src/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java
index d01026566ca0..9b69674728a8 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkState.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkState.java
@@ -22,7 +22,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Slog;
+import android.util.Log;
/**
* Snapshot of network state.
@@ -83,7 +83,7 @@ public class NetworkState implements Parcelable {
if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) {
if (networkInfo.isRoaming() == networkCapabilities
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
- Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
+ Log.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
+ " and " + networkCapabilities);
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 9e42bbecbe9d..c0f262815b0c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -27,8 +27,10 @@ import com.android.net.module.util.Inet4AddressUtils;
import java.io.FileDescriptor;
import java.math.BigInteger;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.Locale;
import java.util.TreeSet;
@@ -212,7 +214,7 @@ public class NetworkUtils {
@Deprecated
public static InetAddress numericToInetAddress(String addrString)
throws IllegalArgumentException {
- return InetAddress.parseNumericAddress(addrString);
+ return InetAddresses.parseNumericAddress(addrString);
}
/**
@@ -234,7 +236,7 @@ public class NetworkUtils {
try {
String[] pieces = ipAndMaskString.split("/", 2);
prefixLength = Integer.parseInt(pieces[1]);
- address = InetAddress.parseNumericAddress(pieces[0]);
+ address = InetAddresses.parseNumericAddress(pieces[0]);
} catch (NullPointerException e) { // Null string.
} catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
} catch (NumberFormatException e) { // Non-numeric prefix.
@@ -249,6 +251,47 @@ public class NetworkUtils {
}
/**
+ * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+ * @hide
+ *
+ * @deprecated This method is used only for IpPrefix and LinkAddress. Since Android S, use
+ * {@link #parseIpAndMask(String)}, if possible.
+ */
+ @Deprecated
+ public static Pair<InetAddress, Integer> legacyParseIpAndMask(String ipAndMaskString) {
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = ipAndMaskString.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ if (pieces[0] == null || pieces[0].isEmpty()) {
+ final byte[] bytes = new byte[16];
+ bytes[15] = 1;
+ return new Pair<InetAddress, Integer>(Inet6Address.getByAddress(
+ "ip6-localhost"/* host */, bytes, 0 /* scope_id */), prefixLength);
+ }
+
+ if (pieces[0].startsWith("[")
+ && pieces[0].endsWith("]")
+ && pieces[0].indexOf(':') != -1) {
+ pieces[0] = pieces[0].substring(1, pieces[0].length() - 1);
+ }
+ address = InetAddresses.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ } catch (UnknownHostException e) { // IP address length is illegal
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+ }
+
+ return new Pair<InetAddress, Integer>(address, prefixLength);
+ }
+
+ /**
* Convert a 32 char hex string into a Inet6Address.
* throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
* made into an Inet6Address
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 48bd29769f83..5a76cd6d6b0f 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -73,6 +73,14 @@ public final class OemNetworkPreferences implements Parcelable {
private final Bundle mNetworkMappings;
/**
+ * Return whether this object is empty.
+ * @hide
+ */
+ public boolean isEmpty() {
+ return mNetworkMappings.keySet().size() == 0;
+ }
+
+ /**
* Return the currently built application package name to {@link OemNetworkPreference} mappings.
* @return the current network preferences map.
*/
diff --git a/core/java/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
index bcfdd7ef09cc..ca6d012dfe7c 100644
--- a/core/java/android/net/ParseException.java
+++ b/packages/Connectivity/framework/src/android/net/ParseException.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
/**
* Thrown when parsing failed.
@@ -25,12 +26,16 @@ import android.annotation.NonNull;
public class ParseException extends RuntimeException {
public String response;
- ParseException(@NonNull String response) {
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public ParseException(@NonNull String response) {
super(response);
this.response = response;
}
- ParseException(@NonNull String response, @NonNull Throwable cause) {
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public ParseException(@NonNull String response, @NonNull Throwable cause) {
super(response, cause);
this.response = response;
}
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 229db0d717cd..745e20f1542e 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -129,7 +129,7 @@ public class ProxyInfo implements Parcelable {
}
/**
- * Only used in PacProxyInstaller after Local Proxy is bound.
+ * Only used in PacProxyService after Local Proxy is bound.
* @hide
*/
public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
index d37c4691ddde..53d966937a70 100644
--- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
@@ -92,7 +92,7 @@ public final class QosSocketInfo implements Parcelable {
Objects.requireNonNull(socket, "socket cannot be null");
mNetwork = Objects.requireNonNull(network, "network cannot be null");
- mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+ mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
mLocalSocketAddress =
new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
}
@@ -114,10 +114,10 @@ public final class QosSocketInfo implements Parcelable {
try {
return new InetSocketAddress(InetAddress.getByAddress(address), port);
} catch (final UnknownHostException e) {
- /* The catch block was purposely left empty. UnknownHostException will never be thrown
+ /* This can never happen. UnknownHostException will never be thrown
since the address provided is numeric and non-null. */
+ throw new RuntimeException("UnknownHostException on numeric address", e);
}
- return new InetSocketAddress();
}
@Override
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index 340141b78aa5..cd8f4c06de65 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -22,9 +22,6 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.SparseArray;
-
-import com.android.internal.util.MessageUtils;
import java.util.Objects;
@@ -38,13 +35,10 @@ import java.util.Objects;
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class VpnTransportInfo implements TransportInfo, Parcelable {
- private static final SparseArray<String> sTypeToString =
- MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
-
/** Type of this VPN. */
- @VpnManager.VpnType public final int type;
+ public final int type;
- public VpnTransportInfo(@VpnManager.VpnType int type) {
+ public VpnTransportInfo(int type) {
this.type = type;
}
@@ -63,8 +57,7 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable {
@Override
public String toString() {
- final String typeString = sTypeToString.get(type, "VPN_TYPE_???");
- return String.format("VpnTransportInfo{%s}", typeString);
+ return String.format("VpnTransportInfo{type=%d}", type);
}
@Override
diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
index bf5b26e278f9..85b24713f256 100644
--- a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
@@ -19,12 +19,12 @@ package android.net.apf;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Context;
import android.content.res.Resources;
+import android.net.ConnectivityResources;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.R;
-
/**
* APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible
* way to drop unwanted network packets to save power.
@@ -36,6 +36,8 @@ import com.android.internal.R;
*/
@SystemApi
public final class ApfCapabilities implements Parcelable {
+ private static ConnectivityResources sResources;
+
/**
* Version of APF instruction set supported for packet filtering. 0 indicates no support for
* packet filtering using APF programs.
@@ -65,6 +67,14 @@ public final class ApfCapabilities implements Parcelable {
apfPacketFormat = in.readInt();
}
+ @NonNull
+ private static synchronized ConnectivityResources getResources(@NonNull Context ctx) {
+ if (sResources == null) {
+ sResources = new ConnectivityResources(ctx);
+ }
+ return sResources;
+ }
+
@Override
public int describeContents() {
@@ -121,13 +131,43 @@ public final class ApfCapabilities implements Parcelable {
* @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
*/
public static boolean getApfDrop8023Frames() {
- return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames);
+ // TODO(b/183076074): remove reading resources from system resources
+ final Resources systemRes = Resources.getSystem();
+ final int id = systemRes.getIdentifier("config_apfDrop802_3Frames", "bool", "android");
+ return systemRes.getBoolean(id);
+ }
+
+ /**
+ * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
+ * @hide
+ */
+ public static boolean getApfDrop8023Frames(@NonNull Context context) {
+ final ConnectivityResources res = getResources(context);
+ // TODO(b/183076074): use R.bool.config_apfDrop802_3Frames directly
+ final int id = res.get().getIdentifier("config_apfDrop802_3Frames", "bool",
+ res.getResourcesContext().getPackageName());
+ return res.get().getBoolean(id);
}
/**
* @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
*/
public static @NonNull int[] getApfEtherTypeBlackList() {
- return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList);
+ // TODO(b/183076074): remove reading resources from system resources
+ final Resources systemRes = Resources.getSystem();
+ final int id = systemRes.getIdentifier("config_apfEthTypeBlackList", "array", "android");
+ return systemRes.getIntArray(id);
+ }
+
+ /**
+ * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
+ * @hide
+ */
+ public static @NonNull int[] getApfEtherTypeDenyList(@NonNull Context context) {
+ final ConnectivityResources res = getResources(context);
+ // TODO(b/183076074): use R.array.config_apfEthTypeDenyList directly
+ final int id = res.get().getIdentifier("config_apfEthTypeDenyList", "array",
+ res.getResourcesContext().getPackageName());
+ return res.get().getIntArray(id);
}
}
diff --git a/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
index bfc4563fbf8f..8d7a0b3d02ed 100644
--- a/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
+++ b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
@@ -19,12 +19,11 @@ package android.net.util;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
+import android.net.ConnectivityResources;
import android.net.NetworkCapabilities;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
-import com.android.internal.R;
-
/**
* Collection of utilities for socket keepalive offload.
*
@@ -52,8 +51,11 @@ public final class KeepaliveUtils {
public static int[] getSupportedKeepalives(@NonNull Context context) {
String[] res = null;
try {
- res = context.getResources().getStringArray(
- R.array.config_networkSupportedKeepaliveCount);
+ final ConnectivityResources connRes = new ConnectivityResources(context);
+ // TODO: use R.id.config_networkSupportedKeepaliveCount directly
+ final int id = connRes.get().getIdentifier("config_networkSupportedKeepaliveCount",
+ "array", connRes.getResourcesContext().getPackageName());
+ res = new ConnectivityResources(context).get().getStringArray(id);
} catch (Resources.NotFoundException unused) {
}
if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 739ddada50b4..0b42a0036925 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -16,8 +16,8 @@
package android.net.util;
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.ConnectivityResources;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
@@ -35,7 +36,6 @@ import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.Log;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Arrays;
@@ -64,6 +64,7 @@ public class MultinetworkPolicyTracker {
private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
private final Context mContext;
+ private final ConnectivityResources mResources;
private final Handler mHandler;
private final Runnable mAvoidBadWifiCallback;
private final List<Uri> mSettingsUris;
@@ -107,11 +108,12 @@ public class MultinetworkPolicyTracker {
public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
mContext = ctx;
+ mResources = new ConnectivityResources(ctx);
mHandler = handler;
mAvoidBadWifiCallback = avoidBadWifiCallback;
mSettingsUris = Arrays.asList(
- Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
- Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
+ Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+ Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
mResolver = mContext.getContentResolver();
mSettingObserver = new SettingObserver();
mBroadcastReceiver = new BroadcastReceiver() {
@@ -160,12 +162,16 @@ public class MultinetworkPolicyTracker {
* Whether the device or carrier configuration disables avoiding bad wifi by default.
*/
public boolean configRestrictsAvoidBadWifi() {
- return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+ // TODO: use R.integer.config_networkAvoidBadWifi directly
+ final int id = mResources.get().getIdentifier("config_networkAvoidBadWifi",
+ "integer", mResources.getResourcesContext().getPackageName());
+ return (getResourcesForActiveSubId().getInteger(id) == 0);
}
@NonNull
private Resources getResourcesForActiveSubId() {
- return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
+ return SubscriptionManager.getResourcesForSubId(
+ mResources.getResourcesContext(), mActiveSubId);
}
/**
@@ -205,8 +211,10 @@ public class MultinetworkPolicyTracker {
* The default (device and carrier-dependent) value for metered multipath preference.
*/
public int configMeteredMultipathPreference() {
- return mContext.getResources().getInteger(
- R.integer.config_networkMeteredMultipathPreference);
+ // TODO: use R.integer.config_networkMeteredMultipathPreference directly
+ final int id = mResources.get().getIdentifier("config_networkMeteredMultipathPreference",
+ "integer", mResources.getResourcesContext().getPackageName());
+ return mResources.get().getInteger(id);
}
public void updateMeteredMultipathPreference() {
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
index f0193db5c2e2..18d26a7e4baf 100644
--- a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -19,11 +19,12 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkScore;
import android.net.QosSession;
import android.telephony.data.EpsBearerQosSessionAttributes;
/**
- * Interface for NetworkAgents to send network network properties.
+ * Interface for NetworkAgents to send network properties.
* @hide
*/
oneway interface INetworkAgentRegistry {
@@ -31,7 +32,7 @@ oneway interface INetworkAgentRegistry {
void sendLinkProperties(in LinkProperties lp);
// TODO: consider replacing this by "markConnected()" and removing
void sendNetworkInfo(in NetworkInfo info);
- void sendScore(int score);
+ void sendScore(in NetworkScore score);
void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
void sendSocketKeepaliveEvent(int slot, int reason);
void sendUnderlyingNetworks(in @nullable List<Network> networks);
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 2fb9f72feab7..f630ceac3662 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -60,12 +60,14 @@ java_library {
"services.core",
"services.net",
"unsupportedappusage",
+ "ServiceConnectivityResources",
],
static_libs: [
"modules-utils-os",
"net-utils-device-common",
"net-utils-framework-common",
"netd-client",
+ "PlatformProperties",
],
apex_available: [
"//apex_available:platform",
@@ -76,7 +78,7 @@ java_library {
java_library {
name: "service-connectivity",
installable: true,
- static_libs:[
+ static_libs: [
"service-connectivity-pre-jarjar",
],
jarjar_rules: "jarjar-rules.txt",
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
new file mode 100644
index 000000000000..fa4501ac7f29
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// APK to hold all the wifi overlayable resources.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+ name: "ServiceConnectivityResources",
+ sdk_version: "module_current",
+ resource_dirs: [
+ "res",
+ ],
+ privileged: true,
+ export_package_resources: true,
+ apex_available: [
+ "com.android.tethering",
+ ],
+ // TODO: use a dedicated cert once generated
+ certificate: "platform",
+}
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
new file mode 100644
index 000000000000..2c303026158e
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<!-- Manifest for connectivity resources APK -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.connectivity.resources"
+ coreApp="true"
+ android:versionCode="1"
+ android:versionName="S-initial">
+ <application
+ android:label="@string/connectivityResourcesAppLabel"
+ android:defaultToDeviceProtectedStorage="true"
+ android:directBootAware="true">
+ <!-- This is only used to identify this app by resolving the action.
+ The activity is never actually triggered. -->
+ <activity android:name="android.app.Activity" android:exported="true" android:enabled="true">
+ <intent-filter>
+ <action android:name="com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml
new file mode 100644
index 000000000000..7e7025fb042f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Configuration values for ConnectivityService
+ DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+ Overlay package following the overlayable.xml configuration in the same directory:
+ https://source.android.com/devices/architecture/rros -->
+<resources>
+ <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+ Internet access. Actual device behaviour is controlled by
+ Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+ <integer translatable="false" name="config_networkAvoidBadWifi">0</integer>
+</resources> \ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml
new file mode 100644
index 000000000000..7e7025fb042f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Configuration values for ConnectivityService
+ DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+ Overlay package following the overlayable.xml configuration in the same directory:
+ https://source.android.com/devices/architecture/rros -->
+<resources>
+ <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+ Internet access. Actual device behaviour is controlled by
+ Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+ <integer translatable="false" name="config_networkAvoidBadWifi">0</integer>
+</resources> \ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 000000000000..7e7025fb042f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Configuration values for ConnectivityService
+ DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+ Overlay package following the overlayable.xml configuration in the same directory:
+ https://source.android.com/devices/architecture/rros -->
+<resources>
+ <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+ Internet access. Actual device behaviour is controlled by
+ Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+ <integer translatable="false" name="config_networkAvoidBadWifi">0</integer>
+</resources> \ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
new file mode 100644
index 000000000000..71674e4dc606
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Configuration values for ConnectivityService
+ DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+ Overlay package following the overlayable.xml configuration in the same directory:
+ https://source.android.com/devices/architecture/rros -->
+<resources>
+
+ <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
+ If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+ and if that value is empty, the framework will use a hard-coded default.
+ This is *NOT* a URL that will always be used by the system network validation to detect
+ captive portals: NetworkMonitor may use different strategies and will not necessarily use
+ this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
+ instead. -->
+ <!--suppress CheckTagEmptyBody -->
+ <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
+
+ <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
+ <integer name="config_networkTransitionTimeout">60000</integer>
+
+ <!-- Configuration of network interfaces that support WakeOnLAN -->
+ <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+ <!--
+ <item>wlan0</item>
+ <item>eth0</item>
+ -->
+ </string-array>
+
+ <string-array translatable="false" name="config_legacy_networktype_restore_timers">
+ <item>2,60000</item><!-- mobile_mms -->
+ <item>3,60000</item><!-- mobile_supl -->
+ <item>4,60000</item><!-- mobile_dun -->
+ <item>5,60000</item><!-- mobile_hipri -->
+ <item>10,60000</item><!-- mobile_fota -->
+ <item>11,60000</item><!-- mobile_ims -->
+ <item>12,60000</item><!-- mobile_cbs -->
+ </string-array>
+
+ <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
+ Those frames are identified by the field Eth-type having values
+ less than 0x600 -->
+ <bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
+
+ <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
+ will be dropped
+ TODO: need to put proper values, these are for testing purposes only -->
+ <integer-array translatable="false" name="config_apfEthTypeDenyList">
+ <item>0x88A2</item>
+ <item>0x88A4</item>
+ <item>0x88B8</item>
+ <item>0x88CD</item>
+ <item>0x88E3</item>
+ </integer-array>
+
+ <!-- Default supported concurrent socket keepalive slots per transport type, used by
+ ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive
+ offload slots that should be reserved for privileged access. This string array should be
+ overridden by the device to present the capability of creating socket keepalives. -->
+ <!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] -->
+ <string-array translatable="false" name="config_networkSupportedKeepaliveCount">
+ <item>0,1</item>
+ <item>1,3</item>
+ </string-array>
+
+
+ <!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
+ device behaviour is controlled by the metered multipath preference in
+ ConnectivitySettingsManager. This is the default value of that setting. -->
+ <integer translatable="false" name="config_networkMeteredMultipathPreference">0</integer>
+
+ <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+ Internet access. Actual device behaviour is controlled by
+ Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+ <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
+
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
new file mode 100644
index 000000000000..25e19cedbbba
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <overlayable name="ServiceConnectivityResourcesConfig">
+ <policy type="product|system|vendor">
+ <!-- Configuration values for ConnectivityService -->
+ <item type="array" name="config_legacy_networktype_restore_timers"/>
+ <item type="string" name="config_networkCaptivePortalServerUrl"/>
+ <item type="integer" name="config_networkTransitionTimeout"/>
+ <item type="array" name="config_wakeonlan_supported_interfaces"/>
+ <item type="bool" name="config_apfDrop802_3Frames"/>
+ <item type="array" name="config_apfEthTypeDenyList"/>
+ <item type="integer" name="config_networkMeteredMultipathPreference"/>
+ <item type="array" name="config_networkSupportedKeepaliveCount"/>
+ <item type="integer" name="config_networkAvoidBadWifi"/>
+
+ </policy>
+ </overlayable>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
new file mode 100644
index 000000000000..2c7b99265019
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!-- The System Connectivity Resources package is an internal system package that provides
+ configuration values for system networking that were pre-configured in the device. This
+ is the name of the package to display in the list of system apps. [CHAR LIMIT=40] -->
+ <string name="connectivityResourcesAppLabel">System Connectivity Resources</string>
+</resources> \ No newline at end of file
diff --git a/packages/Connectivity/service/jarjar-rules.txt b/packages/Connectivity/service/jarjar-rules.txt
index d8c60a428ef6..a7b419b020b5 100644
--- a/packages/Connectivity/service/jarjar-rules.txt
+++ b/packages/Connectivity/service/jarjar-rules.txt
@@ -1,3 +1,4 @@
+rule android.sysprop.** com.android.connectivity.sysprop.@1
rule com.android.net.module.util.** com.android.connectivity.net-utils.@1
rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 180dfb672c7f..784a74701b4f 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index d87ea7fcef89..5b7bda4d4f84 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 180dfb672c7f..784a74701b4f 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 3124651fe425..780cb8a72526 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index c1dca5df1b2f..16a946dc7bc6 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -138,9 +138,6 @@ public class DynamicSystemInstallationService extends Service
private long mCurrentPartitionSize;
private long mCurrentPartitionInstalledSize;
- private boolean mJustCancelledByUser;
- private boolean mKeepNotification;
-
// This is for testing only now
private boolean mEnableWhenCompleted;
@@ -174,11 +171,6 @@ public class DynamicSystemInstallationService extends Service
if (cache != null) {
cache.flush();
}
-
- if (!mKeepNotification) {
- // Cancel the persistent notification.
- mNM.cancel(NOTIFICATION_ID);
- }
}
@Override
@@ -231,9 +223,11 @@ public class DynamicSystemInstallationService extends Service
return;
}
+ boolean removeNotification = false;
switch (result) {
case RESULT_CANCELLED:
postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+ removeNotification = true;
break;
case RESULT_ERROR_IO:
@@ -251,7 +245,7 @@ public class DynamicSystemInstallationService extends Service
}
// if it's not successful, reset the task and stop self.
- resetTaskAndStop();
+ resetTaskAndStop(removeNotification);
}
private void executeInstallCommand(Intent intent) {
@@ -302,12 +296,12 @@ public class DynamicSystemInstallationService extends Service
return;
}
- stopForeground(true);
- mJustCancelledByUser = true;
-
if (mInstallTask.cancel(false)) {
- // Will stopSelf() in onResult()
+ // onResult() would call resetTaskAndStop() upon task completion.
Log.d(TAG, "Cancel request filed successfully");
+ // Dismiss the notification as soon as possible as DynamicSystemManager.remove() may
+ // block.
+ stopForeground(STOP_FOREGROUND_REMOVE);
} else {
Log.e(TAG, "Trying to cancel installation while it's already completed.");
}
@@ -322,8 +316,7 @@ public class DynamicSystemInstallationService extends Service
if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
Log.e(TAG, "Trying to discard AOT while there is no complete installation");
// Stop foreground state and dismiss stale notification.
- stopForeground(STOP_FOREGROUND_REMOVE);
- resetTaskAndStop();
+ resetTaskAndStop(true);
return;
}
@@ -331,8 +324,8 @@ public class DynamicSystemInstallationService extends Service
getString(R.string.toast_dynsystem_discarded),
Toast.LENGTH_LONG).show();
- resetTaskAndStop();
postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+ resetTaskAndStop(true);
mDynSystem.remove();
}
@@ -412,12 +405,13 @@ public class DynamicSystemInstallationService extends Service
}
private void resetTaskAndStop() {
- mInstallTask = null;
+ resetTaskAndStop(/* removeNotification= */ false);
+ }
- new Handler().postDelayed(() -> {
- stopForeground(STOP_FOREGROUND_DETACH);
- stopSelf();
- }, 50);
+ private void resetTaskAndStop(boolean removeNotification) {
+ mInstallTask = null;
+ stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROUND_DETACH);
+ stopSelf();
}
private void prepareNotification() {
@@ -525,7 +519,7 @@ public class DynamicSystemInstallationService extends Service
private void postStatus(int status, int cause, Throwable detail) {
String statusString;
String causeString;
- mKeepNotification = false;
+ boolean notifyOnNotificationBar = true;
switch (status) {
case STATUS_NOT_STARTED:
@@ -551,18 +545,16 @@ public class DynamicSystemInstallationService extends Service
break;
case CAUSE_INSTALL_CANCELLED:
causeString = "INSTALL_CANCELLED";
+ notifyOnNotificationBar = false;
break;
case CAUSE_ERROR_IO:
causeString = "ERROR_IO";
- mKeepNotification = true;
break;
case CAUSE_ERROR_INVALID_URL:
causeString = "ERROR_INVALID_URL";
- mKeepNotification = true;
break;
case CAUSE_ERROR_EXCEPTION:
causeString = "ERROR_EXCEPTION";
- mKeepNotification = true;
break;
default:
causeString = "CAUSE_NOT_SPECIFIED";
@@ -571,16 +563,6 @@ public class DynamicSystemInstallationService extends Service
Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
- boolean notifyOnNotificationBar = true;
-
- if (status == STATUS_NOT_STARTED
- && cause == CAUSE_INSTALL_CANCELLED
- && mJustCancelledByUser) {
- // if task is cancelled by user, do not notify them
- notifyOnNotificationBar = false;
- mJustCancelledByUser = false;
- }
-
if (notifyOnNotificationBar) {
mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 4ef5e2b4f090..59ea9f0150bf 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,7 +320,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
}
- private void installScratch() throws IOException, InterruptedException {
+ private void installScratch() throws IOException {
final long scratchSize = mDynSystem.suggestScratchSize();
Thread thread = new Thread() {
@Override
@@ -347,7 +347,11 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
publishProgress(progress);
}
- Thread.sleep(100);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore the error.
+ }
}
if (mInstallationSession == null) {
@@ -361,7 +365,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
}
- private void installUserdata() throws IOException, InterruptedException {
+ private void installUserdata() throws IOException {
Thread thread = new Thread(() -> {
mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
});
@@ -383,7 +387,11 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
publishProgress(progress);
}
- Thread.sleep(100);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore the error.
+ }
}
if (mInstallationSession == null) {
@@ -397,8 +405,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
}
- private void installImages()
- throws IOException, InterruptedException, ImageValidationException {
+ private void installImages() throws IOException, ImageValidationException {
if (mStream != null) {
if (mIsZip) {
installStreamingZipUpdate();
@@ -410,14 +417,12 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
}
- private void installStreamingGzUpdate()
- throws IOException, InterruptedException, ImageValidationException {
+ private void installStreamingGzUpdate() throws IOException, ImageValidationException {
Log.d(TAG, "To install a streaming GZ update");
installImage("system", mSystemSize, new GZIPInputStream(mStream));
}
- private void installStreamingZipUpdate()
- throws IOException, InterruptedException, ImageValidationException {
+ private void installStreamingZipUpdate() throws IOException, ImageValidationException {
Log.d(TAG, "To install a streaming ZIP update");
ZipInputStream zis = new ZipInputStream(mStream);
@@ -432,8 +437,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
}
- private void installLocalZipUpdate()
- throws IOException, InterruptedException, ImageValidationException {
+ private void installLocalZipUpdate() throws IOException, ImageValidationException {
Log.d(TAG, "To install a local ZIP update");
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -449,7 +453,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
private boolean installImageFromAnEntry(ZipEntry entry, InputStream is)
- throws IOException, InterruptedException, ImageValidationException {
+ throws IOException, ImageValidationException {
String name = entry.getName();
Log.d(TAG, "ZipEntry: " + name);
@@ -473,7 +477,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
}
private void installImage(String partitionName, long uncompressedSize, InputStream is)
- throws IOException, InterruptedException, ImageValidationException {
+ throws IOException, ImageValidationException {
SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
@@ -504,7 +508,11 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
return;
}
- Thread.sleep(100);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore the error.
+ }
}
if (mInstallationSession == null) {
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index f2a0e1cb83bd..68ce7d963242 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -17,8 +17,8 @@ android_library {
// TODO(b/149540986): revert this change.
static_libs: [
- // All other dependent components should be put in
- // "SettingsLibDependenciesWithoutWifiTracker".
+ // All other dependent components should be put in
+ // "SettingsLibDependenciesWithoutWifiTracker".
"WifiTrackerLib",
],
@@ -27,7 +27,10 @@ android_library {
resource_dirs: ["res"],
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
min_sdk_version: "21",
@@ -67,6 +70,7 @@ java_defaults {
"SettingsLibFooterPreference",
"SettingsLibUsageProgressBarPreference",
"SettingsLibCollapsingToolbarBaseActivity",
+ "SettingsLibTwoTargetPreference",
],
}
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
index 781bfcdbc75e..1ccf4175bac5 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
@@ -51,6 +51,7 @@ public class AppSwitchPreference extends SwitchPreference {
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
+ setSingleLineTitle(true);
super.onBindViewHolder(holder);
final View switchView = holder.findViewById(android.R.id.switch_widget);
if (switchView != null) {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index ed49bf4d5385..231babea97c2 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -20,4 +20,8 @@ android_library {
],
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.cellbroadcast",
+ ],
}
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down_24dp.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
index 827d0b583388..054452629c88 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down_24dp.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
@@ -16,11 +16,11 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportWidth="24"
- android:viewportHeight="24"
+ android:viewportWidth="18"
+ android:viewportHeight="18"
android:width="24dp"
android:height="24dp">
<path
android:pathData="M7 10l5 5 5 -5z"
- android:fillColor="?android:attr/textColorPrimary"/>
-</vector> \ No newline at end of file
+ android:fillColor="@android:color/black"/>
+</vector>
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
index 263c16b0749c..b38e3e3e89c1 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
@@ -24,21 +24,21 @@
android:bottom="8dp">
<shape>
<corners
- android:radius="20dp"/>
+ android:radius="28dp"/>
<solid
- android:color="?android:attr/colorPrimary"/>
+ android:color="?android:attr/colorAccent"/>
<stroke
- android:color="#1f000000"
+ android:color="?android:attr/colorPrimary"
android:width="1dp"/>
<size
- android:height="32dp"/>
+ android:height="@dimen/spinner_height"/>
</shape>
</item>
<item
android:gravity="center|end"
- android:width="24dp"
- android:height="24dp"
- android:end="4dp"
- android:drawable="@drawable/arrow_drop_down_24dp"/>
+ android:width="18dp"
+ android:height="18dp"
+ android:end="8dp"
+ android:drawable="@drawable/arrow_drop_down"/>
</layer-list> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml
new file mode 100644
index 000000000000..8cac988632bc
--- /dev/null
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape>
+ <solid
+ android:color="?android:attr/colorAccent"/>
+ </shape>
+ </item>
+
+ <item>
+ <shape>
+ <solid android:color="#BBFFFFFF"/>
+ </shape>
+ </item>
+
+</layer-list>
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml
new file mode 100644
index 000000000000..a342c840cfbe
--- /dev/null
+++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ style="@style/SettingsSpinnerTitleBar"
+ android:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/settings_spinner_dropdown_background"/>
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml
index bdd370fe04ee..75de34e86bc4 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml
@@ -19,7 +19,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="@style/SettingsSpinnerTitleBar"
- android:maxLines="1"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"/>
+ android:layout_height="wrap_content"/>
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml
index 1ccb176b8689..d526df6bedd4 100644
--- a/packages/SystemUI/res/drawable/controls_dialog_bg.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,8 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorBackground" />
- <corners android:radius="@dimen/notification_corner_radius" />
-</shape>
+
+<resources>
+ <dimen name="spinner_height">36dp</dimen>
+ <dimen name="spinner_padding_top_or_bottom">8dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
index 8af20e20ede9..f665f3836002 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
@@ -18,9 +18,13 @@
<resources>
<style name="SettingsSpinnerTitleBar">
<item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+ <item name="android:textColor">@android:color/black</item>
+ <item name="android:maxLines">1</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:minHeight">@dimen/spinner_height</item>
<item name="android:paddingStart">16dp</item>
<item name="android:paddingEnd">36dp</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingBottom">8dp</item>
+ <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
+ <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item>
</style>
-</resources> \ No newline at end of file
+</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
index 5ef8f7ac792b..0be80a9fd466 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
@@ -121,4 +121,11 @@ public class SettingsSpinner extends Spinner {
int mode) {
super(context, attrs, defStyleAttr, defStyleRes, mode, null);
}
-} \ No newline at end of file
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setDropDownVerticalOffset(getMeasuredHeight() - (int) getContext().getResources()
+ .getDimension(R.dimen.spinner_padding_top_or_bottom));
+ }
+}
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
index a8ca0d8664f3..83da512ce879 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
@@ -30,8 +30,7 @@ import com.android.settingslib.widget.R;
public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> {
private static final int DEFAULT_RESOURCE = R.layout.settings_spinner_view;
- private static final int DFAULT_DROPDOWN_RESOURCE =
- android.R.layout.simple_spinner_dropdown_item;
+ private static final int DFAULT_DROPDOWN_RESOURCE = R.layout.settings_spinner_dropdown_view;
private final LayoutInflater mDefaultInflater;
/**
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index bda863a71453..73459c277df1 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -18,4 +18,8 @@ android_library {
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.cellbroadcast",
+ ],
}
diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp
new file mode 100644
index 000000000000..f2e79b9ab53b
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibTwoTargetPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.preference_preference",
+ ],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml
new file mode 100644
index 000000000000..120b0859a70a
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<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/res/layout/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml
index ff6a22d5523c..21fcedcc01b6 100644
--- a/packages/SettingsLib/res/layout/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 The Android Open Source Project
+ Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SettingsLib/res/layout/preference_two_target_divider.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml
index b81dd83d2586..bd477f8068ff 100644
--- a/packages/SettingsLib/res/layout/preference_two_target_divider.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 The Android Open Source Project
+ Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml b/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml
new file mode 100644
index 000000000000..32a865905267
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+<resources>
+
+ <dimen name="two_target_pref_small_icon_size">24dp</dimen>
+ <dimen name="two_target_pref_medium_icon_size">32dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
index 02895a479352..9130662021d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
+++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,21 +14,24 @@
* limitations under the License.
*/
-package com.android.settingslib;
+package com.android.settingslib.widget;
-import android.annotation.IntDef;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.annotation.IntDef;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * The Base preference with two target areas divided by a vertical divider
+ */
public class TwoTargetPreference extends Preference {
@IntDef({ICON_SIZE_DEFAULT, ICON_SIZE_MEDIUM, ICON_SIZE_SMALL})
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1a67b5e7dea7..0ee44f81cd0e 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rooi-groen)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (blou-geel)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Kleurregstelling"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Verstel hoe kleure op jou toestel vertoon. Dit kan nuttig wees wanneer jy:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Kleure meer akkuraat wil sien&lt;/li&gt; &lt;li&gt; Kleure wil verwyder om jou te help fokus&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Foonluidspreker"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string>
<string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 97abce186d96..9fb676fc3456 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ፕሮታኖማሊ (ቀይ-አረንጓዴ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ትራይታኖማሊ (ሰማያዊ-ቢጫ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"የቀለም ማስተካከያ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ቀለሞች በመሣሪያዎ ላይ እንዴት እንደሚታዩ ያስተካክሉ። የሚከተሉትን ለማድረግ በሚፈልጉበት ጊዜ ይህ ጠቃሚ ሊሆን ይችላል፦&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ቀለሞችን የበለጠ ትክክለኛ በሆነ መልኩ ለመመልከት&lt;/li&gt; &lt;li&gt; ትኩረት ለማድረግ እንዲረዳዎ ቀለሞችን ለማስወገድ&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"የስልክ ድምጽ ማጉያ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string>
<string name="help_label" msgid="3528360748637781274">"እገዛ እና ግብረመልስ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f9e12c7149c0..cdb418aa4b54 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"غطش الأحمر (الأحمر والأخضر)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"غمش الأزرق (الأزرق والأصفر)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"تصحيح الألوان"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏يمكنك تعديل كيفية عرض الألوان على جهازك. يساعدك هذا الخيار عندما تريد تنفيذ ما يلي:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; عرض الألوان بمزيد من الدقة&lt;/li&gt; &lt;li&gt; إزالة الألوان لمساعدتك على التركيز&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا"</string>
@@ -520,8 +519,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
<string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات والآراء"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index efd813a010e0..442a19da2971 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্ৰ’টানোমালি (ৰঙা-সেউজীয়া)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্ৰাইটান\'মেলী (নীলা-হালধীয়া)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ৰং শুধৰণী"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"আপোনাৰ ডিভাইচত ৰংবোৰ কেনেকৈ প্ৰদৰ্শিত হয় সেয়া মিলাওক। এইটো আপুনি এই কাৰ্য কৰিবলৈ বিচাৰিলে সহায়ক হ\'ব পাৰে:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ৰং অধিক সঠিককৈ চাবলৈ বিচৰা&lt;/li&gt; &lt;li&gt; আপোনাক মনোযোগ দিয়াত সহায় কৰিবলৈ ৰং আঁতৰোৱা&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফ’নৰ স্পীকাৰ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string>
<string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7a5aec4d3b9b..950a5ae71cb5 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Пратанамалія (чырвоны-зялёны)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Трытанамалія (сіні-жоўты)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Карэкцыя колеру"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Наладзьце адлюстраванне колераў на экране прылады. Гэта налада можа быць карыснай, калі вы захочаце:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; бачыць колеры больш дакладна;&lt;/li&gt; &lt;li&gt; выдаліць колеры, якія перашкаджаюць вам сканцэнтравацца&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Зараду хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Дынамік тэлефона"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string>
<string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 675d1794ae1a..4cc9abf7f0b3 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্রোটানোম্যালি (লাল-সবুজ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্রিট্যানোম্যালি (নীল-হলুদ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"রঙ সংশোধন"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"আপনার ডিভাইসে রঙগুলি কেমন দেখাবে তা অ্যাডজাস্ট করুন। যেক্ষেত্রে এটি আপনাকে সহায়তা করতে পারে:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; আরও নির্ভুলভাবে রঙ দেখতে&lt;/li&gt; &lt;li&gt; রঙ সরিয়ে দিলে ফোকাস করতে সহায়তা করবে&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফোনের স্পিকার"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
<string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 97083ad15519..e4bc5a199f87 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno-zeleno)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo-žuto)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Ispravka boje"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Podešavanje načina na koji se boje prikazuju na uređaju. To može biti korisno kada želite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; tačnije prikazati boje&lt;/li&gt; &lt;li&gt; ukloniti boje da se lakše fokusirate&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 4889220a8894..fa441a208b66 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermell-verd)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (blau-groc)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correcció de color"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta com es mostren els colors al teu dispositiu. Això pot ser útil quan vulguis:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Veure els colors amb més claredat.&lt;/li&gt; &lt;li&gt; Suprimir colors per poder enfocar més fàcilment.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altaveu del telèfon"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Desactiva el dispositiu i torna\'l a activar."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index e857e60ed224..717b6817f767 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomálie (červená a zelená)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomálie (modrá a žlutá)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekce barev"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Upravte zobrazování barev na svém zařízení. To se může hodit, když chcete:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Aby se barvy zobrazovaly přesněji&lt;/li&gt; &lt;li&gt; Odstranit barvy, abyste se mohli lépe soustředit&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokud tuto funkci nevypnete"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefonu"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string>
<string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 53e21ba3abc2..6b17db1137d9 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopi (rød-grøn)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopi (blå-gul)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korriger farver"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Juster, hvordan farverne vises på skærmen. Dette kan være nyttigt, når du vil:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Se farver mere nøjagtigt&lt;/li&gt; &lt;li&gt; Fjerne farver, så du bedre kan fokusere&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens højttaler"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string>
@@ -541,7 +539,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"Begrænset profil"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"Vil du tilføje en ny bruger?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enhed med andre ved at oprette ekstra brugere. Hver bruger har sit personlige område, som kan tilpasses med apps, baggrund osv. Brugerne kan også justere enhedsindstillinger, som for eksempel Wi-Fi, som påvirker alle.\n\nNår du tilføjer en ny bruger, skal vedkommende konfigurere sit område.\n\nAlle brugere kan opdatere apps for alle andre brugere. Indstillinger og tjenester for hjælpefunktioner overføres muligvis ikke til den nye bruger."</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit rum.\n\nEnhver bruger kan opdatere apps for alle andre brugere."</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit rum.\n\nAlle brugere kan opdatere apps for alle de andre brugere."</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Vil du konfigurere brugeren nu?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Sørg for, at brugeren har mulighed for at tage enheden og konfigurere sit eget rum"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Vil du oprette en profil nu?"</string>
@@ -557,7 +555,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string>
<string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index a4a976fcf34d..bb11ed10ad1d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (Rot-Grün-Sehschwäche)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (Blau-Gelb-Sehschwäche)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Farbkorrektur"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Hier kannst du anpassen, wie Farben auf deinem Gerät dargestellt werden sollen. Das kann in folgenden Fällen hilfreich sein:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Wenn Farben genauer dargestellt werden sollen&lt;/li&gt; &lt;li&gt; Wenn du Farben entfernen möchtest, um dich besser konzentrieren zu können&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Smartphone-Lautsprecher"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus &amp; und wieder ein."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
<string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 71cf5cbea0f2..fb294c8258fc 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta cómo se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieres:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver colores con más exactitud&lt;/li&gt; &lt;li&gt; Quitar colores para mejorar tu concentración&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a7d7b215e3a8..2c5eff2dc730 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta el modo en que se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieras hacer lo siguiente:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver los colores con más precisión&lt;/li&gt; &lt;li&gt; Eliminar colores para ayudarte a mantener la concentración&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index add25f839d21..116faad8431a 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaalia (punane-roheline)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaalia (sinine-kollane)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värvide korrigeerimine"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Kohandage seadmes värvide kuvamist. Sellest võib olla kasu, kui soovite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; värve täpsemalt näha;&lt;/li&gt; &lt;li&gt; värve eemaldada, et paremini keskenduda.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoni kõlar"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string>
<string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index c95d15761561..bfb4efccb499 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopia (urdin-horia)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Doitu nola bistaratzen diren koloreak gailuan. Kasu hauetan izan daiteke lagungarria:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Koloreak zehatzago ikusi nahi dituzunean.&lt;/li&gt; &lt;li&gt; Hobeto fokuratzeko, koloreak kendu nahi dituzunean.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonoaren bozgorailua"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazoren bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
<string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 631ff5e3cbc4..f3018ba1e1e8 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"قرمزدشواربینی (قرمز-سبز)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"آبی‌دشواربینی (آبی-زرد)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"تصحیح رنگ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏نحوه نمایش رنگ‌ها را در دستگاهتان تنظیم می‌کند. این ویژگی می‌تواند در موارد زیر مفید باشد:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; وقتی می‌خواهید رنگ‌ها را با دقت بیشتری ببینید&lt;/li&gt; &lt;li&gt; وقتی می‌خواهید رنگ‌ها را حذف کنید تا تمرکز بیشتری داشته باشید"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانی‌که آن را خاموش کنید"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"هم‌اکنون"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"بلندگوی تلفن"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string>
<string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7591c911b96e..7089937c5a3f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (puna-vihersokeus)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (sini-keltasokeus)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värinkorjaus"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Muuta värien näkymistä laitteellasi. Tästä voi olla hyötyä, kun haluat&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; nähdä värit tarkemmin&lt;/li&gt; &lt;li&gt; poistaa värejä voidaksesi keskittyä paremmin&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Puhelimen kaiutin"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string>
<string name="help_label" msgid="3528360748637781274">"Ohje ja palaute"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a390257c8ce4..ac8addef49f8 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu/jaune)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustez l\'affichage des couleurs sur votre appareil. Ce paramètre peut être utile si vous voulez :&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Mieux distinguer les couleurs&lt;/li&gt; &lt;li&gt; Enlever les couleurs pour vous aider à vous concentrer&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8cf8c6ee5ee6..e4e4293d9edb 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu-jaune)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustez l\'affichage des couleurs sur votre appareil. Cette option peut vous être utile pour :&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; accentuer la précision des couleurs ;&lt;/li&gt; &lt;li&gt; supprimer les couleurs pour mieux vous concentrer.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index c1ba51d798f1..062b7b312430 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (vermello-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección da cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Axusta a maneira en que se mostran as cores no teu dispositivo. Esta opción pode resultarche útil se queres:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver mellor as cores&lt;/li&gt; &lt;li&gt; Quitar as cores para concentrarte mellor&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altofalante do teléfono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 94d83e0577bf..174d0a1db13b 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"પ્રોટેનોમલી (લાલ-લીલો)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ટ્રાઇટેનોમલી(વાદળી-પીળો)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"રંગ સુધારણા"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"તમારા ડિવાઇસ પર રંગો કેવી રીતે બતાવવામાં આવે તેની ગોઠવણી કરો. આ ત્યારે સહાયરૂપ થઈ શકે છે જ્યારે તમારે:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; રંગો વધુ યોગ્ય રીતે જોવા હોય&lt;/li&gt; &lt;li&gt; તમને ફોકસ કરવામાં સહાયતા રહે તે માટે રંગો કાઢી નાખવા હોય&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ફોન સ્પીકર"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string>
<string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index e99d4cb94e8b..3d06baada7a8 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno – zeleno)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo – žuto)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcija boje"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Prilagodite način prikazivanja boja na svojem uređaju. To može biti korisno kad želite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; vidjeti boje točnije&lt;/li&gt; &lt;li&gt; ukloniti boje kako biste se lakše usredotočili&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 503ee6020361..546a038090c4 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (piros– zöld)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (kék–sárga)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Színkorrekció"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Korrigálhatja a színek megjelenítését az eszközén. Ez a következő esetekben lehet hasznos:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ha pontosabb színeket szeretne látni;&lt;/li&gt; &lt;li&gt; ha szeretné eltávolítani a színeket, hogy jobban tudjon koncentrálni.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hangszórója"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string>
<string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 3686dd171964..7c0963d61ab2 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Պրոտանոմալիա (կարմիր-կանաչ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Տրիտանոմալիա (կապույտ-դեղին)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Գունաշտկում"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Կարգավորեք գույների ցուցադրումը ձեր սարքում։ Դա կարող է օգտակար լինել, երբ դուք ուզում եք՝&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ավելի հստակ տեսնել գույները&lt;/li&gt; &lt;li&gt; Հեռացնել գույները՝ կենտրոնանալու համար&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև չանջատեք"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Հեռախոսի բարձրախոս"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string>
<string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 035be7d90e05..2f8bb46c0af0 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (merah-hijau)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (biru-kuning)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koreksi warna"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Sesuaikan cara warna ditampilkan di perangkat Anda. Ini dapat bermanfaat saat Anda ingin:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Melihat warna dengan lebih akurat&lt;/li&gt; &lt;li&gt; Menghapus warna untuk membantu Anda fokus&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ponsel"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat &amp; aktifkan kembali"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string>
<string name="help_label" msgid="3528360748637781274">"Bantuan &amp; masukan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 718b5be270a7..ea2925852d6a 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Litblinda (rauðgræn)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Litblinda (blágul)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Litaleiðrétting"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Stilltu litabirtingu í tækinu þínu. Þetta getur gagnast þegar þú vilt:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Sjá liti skýrar&lt;/li&gt; &lt;li&gt; Fjarlægja liti til að fókusa betur&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Símahátalari"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string>
<string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index d2176d905b34..5dd7a520389b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalìa (rosso-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalìa (blu-giallo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correzione del colore"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Regola la modalità di visualizzazione dei colori sul tuo dispositivo. Può essere utile se vuoi:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Vedere i colori con più precisione&lt;/li&gt; &lt;li&gt; Rimuovere colori per mettere a fuoco più facilmente&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlante telefono"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
<string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1357cae170f7..20d1e1bbb71d 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"פרוטנומליה (אדום-ירוק)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"טריטנומליה (כחול-צהוב)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"תיקון צבע"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏ניתן לשנות את האופן שבו צבעים מוצגים במכשיר. שינוי כזה עשוי לעזור:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; להבחין בצבעים בצורה יותר מדויקת&lt;/li&gt; &lt;li&gt; להסיר צבעים מסוימים כדי להתמקד&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"רמקול של טלפון"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string>
<string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 8bb6ba892e6d..37db15c87ab8 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"პროტოანომალია (წითელი-მწვანე)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ტრიტანომალია (ლურჯი-ყვითელი)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ფერის კორექცია"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"დააკორექტირეთ, როგორ გამოჩნდება ფერები თქვენს მოწყობილობაზე. ეს შეიძლება დაგეხმაროთ, როდესაც გსურთ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ფერების მეტი სიზუსტით დანახვა&lt;/li&gt; &lt;li&gt; ფერების მოცილება, რომ უკეთ კონცენტრირდეთ&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ტელეფონის დინამიკი"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string>
<string name="help_label" msgid="3528360748637781274">"დახმარება და გამოხმაურება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 6a2c21eee17f..878966de2cb9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (қызыл-жасыл)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (көк-сары)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Түсті түзету"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Құрылғыңызда түстер қалай көрсетілетінін реттеңіз. Бұл мыналар үшін пайдалы болуы мүмкін:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; түстерді анығырақ көру&lt;/li&gt; &lt;li&gt; зейініңізді жақсарту үшін түстерді өшіру&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string>
@@ -503,7 +502,7 @@
<string name="cancel" msgid="5665114069455378395">"Бас тарту"</string>
<string name="okay" msgid="949938843324579502">"Жарайды"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Мазаламау\" режимін қосу"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ешқашан"</string>
<string name="zen_interruption_level_priority" msgid="5392140786447823299">"Маңыздылары ғана"</string>
<string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефон динамигі"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string>
<string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string>
@@ -557,8 +555,8 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string>
<string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index f4cece4aa038..708879a6d9c3 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើត​អ្នកប្រើប្រាស់ថ្មី…"</string>
<string name="user_nickname" msgid="262624187455825083">"ឈ្មោះ​ហៅក្រៅ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"លុប​​​ភ្ញៀវ"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើស​រូបភាព"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a844ee6d0ad5..daf7b6fa6cdf 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ಪ್ರೊಟನೋಮಲಿ (ಕೆಂಪು-ಹಸಿರು)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ಟ್ರಿಟನೋಮಲಿ (ನೀಲಿ-ಹಳದಿ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಬಣ್ಣಗಳು ಹೇಗೆ ಡಿಸ್‌ಪ್ಲೇ ಆಗುತ್ತವೆ ಎಂಬುದನ್ನು ಹೊಂದಿಸಿ. ನೀವು ಬಣ್ಣಗಳನ್ನು ಹೆಚ್ಚು ನಿಖರವಾಗಿ ನೋಡಲು ಬಯಸಿದಾಗ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ಇದು ಸಹಾಯಕವಾಗಿರುತ್ತದೆ&lt;/li&gt; &lt;li&gt; ನಿಮಗೆ ಗಮನಹರಿಸಲು ಸಹಾಯ ಮಾಡಲು ಬಣ್ಣಗಳನ್ನು ತೆಗೆದುಹಾಕಿ&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
<string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 4ca656798df8..834200d20a4e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (кызыл-жашыл)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (көк-сары)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Түсүн тууралоо"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Түзмөгүңүздө түстөр кантип көрүнөрүн тууралаңыз. Бул төмөнкү учурларда пайдалуу болот:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Түстөрдү даана көрүү&lt;/li&gt; &lt;li&gt; Ынтаа коюу үчүн түстөрдү өчүрүү&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефондун динамиги"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string>
<string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 176c80fbc1d6..0dcf162720ea 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ສີ​ແດງ​-ສີ​ຂຽວ​)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ສີ​ຟ້າ​-ສີ​ເຫຼືອງ​)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ການ​ປັບ​ແຕ່ງ​ສີ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ປັບແກ້ການສະແດງສີຢູ່ອຸປະກອນຂອງທ່ານ. ນີ້ອາດມີປະໂຫຍດໃນເວລາທີ່ທ່ານຕ້ອງການ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ເບິ່ງເຫັນສີໄດ້ຖືກຕ້ອງຍິ່ງຂຶ້ນ&lt;/li&gt; &lt;li&gt; ລຶບສີອອກເພື່ອຊ່ວຍໃຫ້ທ່ານມີສະມາທິ&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ລຳໂພງໂທລະສັບ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string>
<string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b0ab8f449c67..c3f92028c7d7 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (raudona, žalia)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (mėlyna, geltona)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Spalvų taisymas"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Koreguokite, kaip spalvos rodomos jūsų įrenginyje. Tai gali būti naudinga, kai norite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; matyti tikslesnes spalvas;&lt;/li&gt; &lt;li&gt; pašalinti spalvas, kad būtų lengviau susitelkti.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefono garsiakalbis"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string>
<string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2633f13e4e34..5090df0e4bfe 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomālija (sarkans/zaļš)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomālija (zils/dzeltens)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Krāsu korekcija"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Pielāgojiet krāsu attēlojumu jūsu ierīcē. Izmantojiet šo funkciju, lai:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; skatītu precīzāku krāsu attēlojumu;&lt;/li&gt; &lt;li&gt; noņemtu krāsas, kad jāpievēršas kādam uzdevumam.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> — <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Tālruņa skaļrunis"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string>
<string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index e4173d0dcac9..c4890d490bc3 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалија (слепило за црвена и зелена)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалија (слепило за сина и жолта)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекција на бои"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Приспособете го приказот на боите на уредот. Ова е корисно кога сакате:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; да гледате попрецизни бои&lt;/li&gt; &lt;li&gt; да отстраните бои за подобра концентрација&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Неодамнешни"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефонски звучник"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
<string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 3cfe47954a33..829d474191e5 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"പ്രോട്ടാനോമലി (ചുവപ്പ്-പച്ച)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ട്രിട്ടാനോമലി (നീല-മഞ്ഞ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"നിറം ക്രമീകരിക്കൽ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"നിങ്ങളുടെ ഉപകരണത്തിൽ നിറങ്ങൾ എങ്ങനെ പ്രദർശിപ്പിക്കണമെന്ന് ക്രമീകരിക്കുക. ഇനിപ്പറയുന്ന കാര്യങ്ങൾ ചെയ്യാൻ ആഗ്രഹിക്കുമ്പോൾ ഇത് സഹായകരമാകും:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; നിറങ്ങൾ കൂടുതൽ കൃത്യമായി കാണാൻ&lt;/li&gt; &lt;li&gt; ഫോക്കസ് ചെയ്യാൻ നിങ്ങളെ സഹായിക്കുന്നതിന് നിറങ്ങൾ നീക്കം ചെയ്യാൻ&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ഫോൺ സ്‌പീക്കർ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്‌റ്റ് ചെയ്യുന്നതിൽ പ്രശ്‌നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string>
<string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്‌ബാക്കും"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index f8d9089d88e3..e9fe1dd097b9 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"क्षीण रक्तवर्णांधता (लाल-हिरवा)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"रंग दृष्टी कमतरता (निळा-पिवळा)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रंग सुधारणा"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"तुमच्या डिव्हाइसवर रंग कसे प्रदर्शित केले जातात ते अ‍ॅडजस्ट करा. तुम्हाला &lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt;अधिक स्पष्टपणे रंग पाहणे &lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt; तुम्हाला फोकस करण्यात मदत करण्यासाठी रंग काढून टाकणे&lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt; हे करायचे असते तेव्हा उपयुक्त असू शकते."</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनचा स्पीकर"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्‍ट करण्‍यात समस्‍या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string>
<string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f10e5393877f..d80242efce97 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (အနီ-အစိမ်း)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (အပြာ-အဝါ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင်ပြင်ဆင်မှု"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"သင့်စက်ပစ္စည်းတွင် အရောင်များပြသပုံကို ချိန်ညှိပါ။ ၎င်းက အောက်ပါတို့တွင် အသုံးဝင်နိုင်သည်-&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; အရောင်များကို ပိုမိုတိကျစွာ မြင်လိုခြင်း&lt;/li&gt; &lt;li&gt; သင်အာရုံစိုက်နိုင်ရန် အရောင်များကို ဖယ်ရှားခြင်း&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ဖုန်းစပီကာ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string>
<string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 9fb68d0a30ef..7b49b4048e06 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (rød-grønn)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (blå-gul)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Fargekorrigering"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Juster hvordan farger vises på enheten. Dette kan være nyttig når du vil&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; se farger mer nøyaktig&lt;/li&gt; &lt;li&gt; fjerne farger for å gjøre det enklere å fokusere&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhøyttaler"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string>
<string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4c847cfaa2b4..4d380a5baca0 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"प्रोटानेमली (रातो, हरियो)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ट्रिटानोमेली (निलो-पंहेलो)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रङ्ग सुधार"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"तपाईंको यन्त्रमा रङहरू कस्ता देखिन्छन् भन्ने कुरा मिलाउनुहोस्। यो सुविधा निम्न अवस्थामा उपयोगी हुन सक्छ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; तपाईं अझ सटीक रूपमा रङहरू देख्न चाहनुहुन्छ भने&lt;/li&gt; &lt;li&gt; तपाईं कुनै कुरामा ध्यान केन्द्रित गर्न रङहरू हटाउन चाहनुहुन्छ भने&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनको स्पिकर"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि सक्रिय गर्नुहोस्"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string>
<string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string>
@@ -541,7 +539,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबन्धित प्रोफाइल"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"नयाँ प्रयोगकर्ता थप्ने हो?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"तपाईं थप प्रयोगकर्ताहरू सिर्जना गरेर ती प्रयोगकर्तालाई यो यन्त्र प्रयोग गर्न दिन सक्नुहुन्छ। हरेक प्रयोगकर्ताको आफ्नै ठाउँ हुन्छ। उनीहरू यो ठाउँमा आफ्नै एप, वालपेपर आदिका लागि प्रयोग गर्न सक्छन्। उनीहरू सबैजनालाई असर पार्ने Wi-Fi जस्ता यन्त्रका सेटिङहरू पनि परिवर्तन गर्न सक्छन्।\n\nतपाईंले नयाँ प्रयोगकर्ता थप्दा उक्त व्यक्तिले आफ्नो ठाउँ सेटअप गर्नु पर्ने हुन्छ।\n\nसबै प्रयोगकर्ता अन्य सबै प्रयोगकर्ताले प्रयोग गर्ने एपहरू अद्यावधिक गर्न सक्छन्। तर पहुँचसम्बन्धी सेटिङ तथा सेवाहरू नयाँ प्रयोगकर्तामा नसर्न सक्छ।"</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"जब तपाईंले नयाँ प्रयोगकर्ता थप्नुहुन्छ, त्यो व्यक्तिले आफ्नो ठाउँ सेट गर्न आवश्यक छ।\n\nकुनै पनि प्रयोगकर्ताले सबै अन्य प्रयोगकर्ताहरूका लागि एपहरू अद्यावधिक गर्न सक्छन्।"</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"तपाईंले नयाँ प्रयोगकर्ता थप्नुभयो भने ती प्रयोगकर्ताले आफ्नो स्पेस सेट गर्नु पर्ने हुन्छ।\n\nसबै प्रयोगकर्ताले अरू प्रयोगकर्ताका एपहरू अपडेट गर्न सक्छन्।"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"अहिले प्रयोगकर्ता सेटअप गर्ने हो?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"यी व्यक्ति यन्त्र यो यन्त्र चलाउन र आफ्नो ठाउँ सेट गर्न उपलब्ध छन् भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"अहिले प्रोफाइल सेटअप गर्ने हो?"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 4cb755ba468c..518aaa9474a8 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -371,7 +371,7 @@
<string name="app_process_limit_title" msgid="8361367869453043007">"Achtergrondproceslimiet"</string>
<string name="show_all_anrs" msgid="9160563836616468726">"ANR\'s op de achtergrond"</string>
<string name="show_all_anrs_summary" msgid="8562788834431971392">"Dialoogvenster \'App reageert niet\' weergeven voor achtergrond-apps"</string>
- <string name="show_notification_channel_warnings" msgid="3448282400127597331">"Kanaalwaarschuwingen voor meldingen weergeven"</string>
+ <string name="show_notification_channel_warnings" msgid="3448282400127597331">"Kanaalwaarschuwingen voor meldingen tonen"</string>
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Geeft een waarschuwing op het scherm weer wanneer een app een melding post zonder geldig kanaal"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"Toestaan van apps op externe opslag afdwingen"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"Hiermee komt elke app in aanmerking voor schrijven naar externe opslag, ongeacht de manifestwaarden"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index b7c93e55163c..b0fff21d4a07 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ପ୍ରୋଟାନୋମାଲି (ଲାଲ୍‌-ସବୁଜ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ନୀଳ-ହଳଦିଆ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ଆପଣଙ୍କ ଡିଭାଇସରେ ରଙ୍ଗଗୁଡ଼ିକ କିପରି ଡିସପ୍ଲେ ହୁଏ ତାହା ଆଡଜଷ୍ଟ କରନ୍ତୁ। ଆପଣ ଏହା କରିବାକୁ ଚାହିଁଲେ ଏହା ଉପଯୋଗୀ ହୋଇପାରେ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ଆହୁରି ସଠିକ୍ ଭାବେ ରଙ୍ଗଗୁଡ଼ିକୁ ଦେଖିବା&lt;/li&gt; &lt;li&gt; ଆପଣଙ୍କୁ ଫୋକସ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ ରଙ୍ଗଗୁଡ଼ିକୁ କାଢ଼ିବା&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍‌ରାଇଡ୍‌ କରାଯାଇଛି"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ଫୋନ୍ ସ୍ପିକର୍"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
<string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 896a10034383..71e2dbad01e1 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ਲਾਲ-ਹਰਾ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ਨੀਲਾ-ਪੀਲਾ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ਰੰਗ ਸੁਧਾਈ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਰੰਗਾਂ ਨੂੰ ਦਿਖਾਉਣ ਦੇ ਤਰੀਕੇ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ। ਇਹ ਉਦੋਂ ਲਾਹੇਵੰਦ ਹੋ ਸਕਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਇਹ ਕਰਨਾ ਚਾਹੋਗੇ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ਰੰਗਾਂ ਨੂੰ ਹੋਰ ਸਟੀਕਤਾ ਨਾਲ ਦੇਖਣਾ&lt;/li&gt; &lt;li&gt; ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਲਈ ਰੰਗਾਂ ਨੂੰ ਹਟਾਉਣਾ&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string>
<string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 663f3fc592da..c8d7287c9966 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (czerwony-zielony)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (niebieski-żółty)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcja kolorów"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Dostosuj sposób wyświetlania kolorów na ekranie urządzenia. Może to być pomocne, gdy chcesz:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; dokładniej widzieć kolory;,&lt;/li&gt; &lt;li&gt; usunąć wybrane kolory, aby móc skuteczniej się skupić.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Głośnik telefonu"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 8eab928c7fb5..1d6624dd5adf 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção de cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste as cores exibidas no seu dispositivo. Esta opção pode ser útil quando você quer:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ver cores com mais precisão;&lt;/li&gt; &lt;li&gt; remover cores para se concentrar melhor.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f28d70a71ce7..66d4b23c59d4 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção da cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste a visualização das cores no dispositivo. Isto pode ser útil quando pretender:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver cores com maior precisão&lt;/li&gt; &lt;li&gt; Remover cores para melhorar a sua concentração&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altifalante do telemóvel"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e comentários"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 8eab928c7fb5..1d6624dd5adf 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção de cor"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste as cores exibidas no seu dispositivo. Esta opção pode ser útil quando você quer:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ver cores com mais precisão;&lt;/li&gt; &lt;li&gt; remover cores para se concentrar melhor.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0a4e46209939..3e77ab6825a6 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (roșu-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (albastru-galben)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corecția culorii"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustați modul în care se afișează culorile pe dispozitiv. Acest lucru poate fi util când doriți să:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; vedeți culorile mai bine&lt;/li&gt; &lt;li&gt; eliminați culorile pentru a vă ajuta să vă concentrați&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivați"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Difuzorul telefonului"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Opriți și reporniți dispozitivul."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string>
<string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index b030715da04f..b342287612b5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (красный/зеленый)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (синий/желтый)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Коррекция цвета"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Настройте цветопередачу на экране устройства. Эта функция может помочь:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; сделать цвета более четкими;&lt;/li&gt; &lt;li&gt; убрать цвета, чтобы вам проще было сфокусироваться.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"Уровень заряда – <xliff:g id="PERCENTAGE">%1$s</xliff:g>. <xliff:g id="TIME_STRING">%2$s</xliff:g>."</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Встроенный динамик"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string>
<string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string>
@@ -559,7 +557,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string>
<string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index d9d53515351f..b24ac8e1587e 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (červená a zelená)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (modrá a žltá)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Úprava farieb"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Upravte si zobrazovanie farieb v zariadení. Môže to byť užitočné, ak chcete:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; zobraziť presnejšie viac farieb;y&lt;/li&gt; &lt;li&gt; odstrániť farby, aby ste sa mohli lepšie sústrediť.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefónu"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string>
<string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 81b1b60ac7fd..626402ae7a10 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (rdeča – zelena)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (modra – rumena)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Popravljanje barv"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Prilagodite prikaz barv v napravi. To je uporabno, ko želite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; videti bolj prave barve;&lt;/li&gt; &lt;li&gt; odstraniti barve, da se lažje osredotočite.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvočnik telefona"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index b71e51f35bb4..4f22348067c4 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (e kuqe - e gjelbër)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (e kaltër - e verdhë)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korrigjimi i ngjyrës"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Rregullo mënyrën se si ngjyrat afishohen në pajisjen tënde. Kjo mund të jetë e dobishme kur dëshiron që:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; T\'i shikosh ngjyrat me më shumë saktësi&lt;/li&gt; &lt;li&gt; T\'i heqësh ngjyrat për të të ndihmuar të fokusohesh&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Deri sa ta çaktivizosh"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlanti i telefonit"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
<string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 2ec2ded3d34f..069b6a577055 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (rött-grönt)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (blått-gult)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Färgkorrigering"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ändra hur färger visas på enheten. Det kan vara ett bra hjälpmedel när du vill&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; att färger ska visas mer exakt&lt;/li&gt; &lt;li&gt; ta bort färger för att fokusera bättre&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens högtalare"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 410a4aa0bddc..0fb620cefce3 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -555,7 +555,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string>
<string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 14eb25d36a64..3106a69a8e7f 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"வண்ணத்திருத்தம்"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"சாதனத்தில் வண்ணங்கள் காண்பிக்கப்படும் விதத்தைச் சரிசெய்யலாம். இதன் மூலம் நீங்கள் விரும்பும்போதெல்லாம்:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; வண்ணங்களை மிகத் தெளிவாகப் பார்க்கலாம்&lt;/li&gt; &lt;li&gt; கவனம் சிதறாமல் இருக்க வண்ணங்களை நீக்கலாம்&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"மொபைல் ஸ்பீக்கர்"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string>
<string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index e652ab71bba3..1f78c0c240e4 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"కలర్ సరిచేయడం"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"మీ పరికరంపై రంగులు కనిపించే విధానాన్ని అడ్జస్ట్ చేయండి. మీకు కావలసినప్పుడు, ఇది సహాయకరంగా ఉంటుంది:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; మరింత ఖచ్చితంగా రంగులను చూడండి&lt;/li&gt; &lt;li&gt; మీరు ఫోకస్ చేయడంలో సహాయపడటానికి రంగులను తీసివేయండి&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్‌ చేసే వరకు"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
<string name="help_label" msgid="3528360748637781274">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
@@ -557,7 +555,7 @@
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్‌ను క్రియేట్ చేస్తోంది…"</string>
<string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్‌ను జోడించండి"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
<string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 5fbf5d41e40b..ef516c4ccef8 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (kırmızı-yeşil)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (mavi-sarı)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Renk düzeltme"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Renklerin cihazınızda nasıl görüntüleneceğini düzenleyin Bu, şunları yapmak istediğinizde kullanışlı olur:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Renkleri daha doğru görmek&lt;/li&gt; &lt;li&gt; Odaklanmanıza yardımcı olması için renkleri kaldırmak&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hoparlörü"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string>
<string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 4511a4bc9ab5..81528ad32a22 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалія (червоний – зелений)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалія (синій – жовтий)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекція кольору"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Налаштуйте відтворення кольорів на екрані пристрою. Це може бути корисно, якщо ви хочете:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; точніше відтворювати кольори;&lt;/li&gt; &lt;li&gt; вилучити кольори, щоб зосередитися на головному.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Динамік телефона"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
<string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index e61d281a025f..798c885c4a7e 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"‏Protanomaly (سرخ سبز)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"‏Tritanomaly (نیلا پیلا)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"رنگ کی اصلاح"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏آپ کے آلے پر رنگوں کے ڈسپلے ہونے کے طریقے کو ایڈجسٹ کریں۔ یہ خصوصیت درج ذیل کے لیے مددگار ثابت ہو سکتی ہے:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; جب آپ رنگوں کو مزید درست طریقے سے دیکھنا چاہیں &lt;/li&gt; &lt;li&gt; فوکس کرنے میں مدد کے لیے رنگوں کو ہٹادیں&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"فون اسپیکر"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string>
<string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 0730c6cc1b50..6efc0bec50f5 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nik"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7d1bde3d8c5c..67923c5ce407 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"红色弱视(红绿不分)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"蓝色弱视(蓝黄不分)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"调整设备上的颜色显示方式。此设置适用于以下情况:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;想要更准确地查看颜色&lt;/li&gt; &lt;li&gt;想要去除颜色,以便集中注意力&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"手机扬声器"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string>
<string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 06b5cd0dfbaa..bf69b3157e71 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -424,7 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"紅色弱視 (紅綠)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"藍色弱視 (藍黃)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"調整裝置顯示顏色的方式。這項設定適用於以下情況:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; 想讓裝置更準確地顯示顏色&lt;/li&gt; &lt;li&gt; 移除顏色以提高專注力&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"調整裝置顯示顏色嘅方式。呢項設定喺以下情況適用:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; 想令裝置更加準確咁顯示顏色&lt;/li&gt; &lt;li&gt; 移除顏色嚟提高專注力&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -515,7 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直至您關閉為止"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string>
- <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string>
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string>
<string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 2297cbe77fac..7b0274a249bc 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"I-Protanomaly (bomvu-luhlaza)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"I-Tritanomaly (luhlaza okwesibhakabhaka-phuzi)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Ukulungiswa kombala"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Lungisa indlela imibala eboniswa ngayo kudivayisi yakkho. Lokhu kungaba usizo lapho ufuna:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ukubona imibala ngokunembilie&lt;/li&gt; &lt;li&gt; Ukususa imibala ukuze ugxile&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele"</string>
@@ -516,8 +515,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Isipikha sefoni"</string>
- <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
- <skip />
+ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string>
<string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index ef4b97f7743f..9d5b23166190 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -32,9 +32,6 @@
<dimen name="user_spinner_padding_sides">20dp</dimen>
<dimen name="user_spinner_item_height">56dp</dimen>
- <dimen name="two_target_pref_small_icon_size">24dp</dimen>
- <dimen name="two_target_pref_medium_icon_size">32dp</dimen>
-
<!-- Lock icon for preferences locked by admin -->
<dimen name="restricted_icon_padding">4dp</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index efa9f3c16b48..d801f1bbf5e5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1067,8 +1067,8 @@
<![CDATA[
Adjust how colors display on your device. This can be helpful when you want to:<br/><br/>
<ol>
- <li> See colors more accurately</li>
- <li> Remove colors to help you focus</li>
+ <li>&nbsp;See colors more accurately</li>
+ <li>&nbsp;Remove colors to help you focus</li>
</ol>
]]></string>
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index ad7e995412aa..fc8b5879c5fa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -27,6 +27,8 @@ import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.widget.TwoTargetPreference;
+
/**
* Preference class that supports being disabled by a user restriction
* set by a device admin.
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 9c0c80bff3df..bf4242f542da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -23,7 +23,6 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.media.AudioManager;
-import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.TetheringManager;
import android.net.vcn.VcnTransportInfo;
@@ -37,6 +36,7 @@ import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -439,8 +439,7 @@ public class Utils {
}
public static boolean isWifiOnly(Context context) {
- return !context.getSystemService(ConnectivityManager.class)
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ return !context.getSystemService(TelephonyManager.class).isDataCapable();
}
/** Returns if the automatic storage management feature is turned on or not. **/
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 8fd1910041c8..129aca4a689f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -386,7 +386,6 @@ public class BluetoothEventManager {
case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
- case BluetoothDevice.UNBOND_REASON_REMOVED:
errorMsg = R.string.bluetooth_pairing_error_message;
break;
default:
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
index e7a20b3f73a4..c88ed8e96f99 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
@@ -1,6 +1,7 @@
# Default reviewers for this and subdirectories.
andychou@google.com
arcwang@google.com
+changbetty@google.com
goldmanj@google.com
qal@google.com
wengsu@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 092cbf3c7c12..60bcf37304a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,7 +16,6 @@
package com.android.settingslib.net;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
@@ -59,7 +58,6 @@ public class DataUsageController {
PERIOD_BUILDER, Locale.getDefault());
private final Context mContext;
- private final ConnectivityManager mConnectivityManager;
private final INetworkStatsService mStatsService;
private final NetworkPolicyManager mPolicyManager;
private final NetworkStatsManager mNetworkStatsManager;
@@ -71,7 +69,6 @@ public class DataUsageController {
public DataUsageController(Context context) {
mContext = context;
- mConnectivityManager = ConnectivityManager.from(context);
mStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
mPolicyManager = NetworkPolicyManager.from(mContext);
@@ -236,7 +233,7 @@ public class DataUsageController {
public boolean isMobileDataSupported() {
// require both supported network and ready SIM
- return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
+ return getTelephonyManager().isDataCapable()
&& getTelephonyManager().getSimState() == SIM_STATE_READY;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index 0bde5c030eb0..f3b600c8e9c1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -2,6 +2,7 @@
andychou@google.com
arcwang@google.com
asapperstein@google.com
+changbetty@google.com
goldmanj@google.com
qal@google.com
wengsu@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 841a49e6d4fd..cbfd4d8ad07b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -75,7 +75,8 @@ public class WifiStatusTracker {
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
- private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ private final NetworkCallback mNetworkCallback =
+ new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
@Override
public void onAvailable(
Network network, NetworkCapabilities networkCapabilities,
@@ -131,7 +132,8 @@ public class WifiStatusTracker {
}
}
};
- private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
+ private final NetworkCallback mDefaultNetworkCallback =
+ new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
// network is now the default network, and its capabilities are nc.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 6a4d650ebf31..4bff78f4af2c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -353,21 +353,6 @@ public class BluetoothEventManagerTest {
}
@Test
- public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() {
- mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
- mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
- mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
- mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED);
- when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
- when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
-
- mContext.sendBroadcast(mIntent);
-
- verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
- eq(R.string.bluetooth_pairing_error_message));
- }
-
- @Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
index 3f0ba13ce50a..aaec909aa335 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.settingslib;
+package com.android.settingslib.widget;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_DEFAULT;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_SMALL;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_DEFAULT;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_SMALL;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +32,8 @@ import android.widget.LinearLayout;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index ed2b6c92530b..db9b83e04a47 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -153,6 +153,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.DOZE_TAP_SCREEN_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_WAKE_DISPLAY_GESTURE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DOZE_QUICK_PICKUP_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NFC_PAYMENT_DEFAULT_COMPONENT, COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(
Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, NON_NEGATIVE_INTEGER_VALIDATOR);
@@ -280,5 +281,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
new InclusiveFloatRangeValidator(0.0f, 1.0f));
VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6568bffddecc..268603fa8b0d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2445,8 +2445,8 @@ class DatabaseHelper extends SQLiteOpenHelper {
R.bool.def_auto_time_zone); // Sync timezone to NITZ
loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
- ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
- res.getBoolean(R.bool.def_stay_on_while_plugged_in))
+ ("1".equals(SystemProperties.get("ro.boot.qemu"))
+ || res.getBoolean(R.bool.def_stay_on_while_plugged_in))
? 1 : 0);
loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a0b952882162..7288371899ce 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1990,6 +1990,13 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.CARRIER_APPS_HANDLED,
SecureSettingsProto.CARRIER_APPS_HANDLED);
+
+ final long clipboardToken = p.start(SecureSettingsProto.CLIPBOARD);
+ dumpSetting(s, p,
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
+ SecureSettingsProto.Clipboard.SHOW_ACCESS_NOTIFICATIONS);
+ p.end(clipboardToken);
+
dumpSetting(s, p,
Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e427981b87d7..400742ba7d78 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1937,8 +1937,11 @@ public class SettingsProvider extends ContentProvider {
if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
return;
}
- checkReadableAnnotation(settingsType, settingName);
ApplicationInfo ai = getCallingApplicationInfoOrThrow();
+ if (ai.isSystemApp() || ai.isSignedWithPlatformKey()) {
+ return;
+ }
+ checkReadableAnnotation(settingsType, settingName);
if (!ai.isInstantApp()) {
return;
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4dc6d1475c4a..c520568a78e5 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -745,6 +745,7 @@ public class SettingsBackupTest {
Settings.Secure.SILENCE_GESTURE,
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
+ Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
Settings.Secure.TAP_GESTURE,
Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a28a1e32a2a5..db38ff64cb04 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -187,6 +187,7 @@
<uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
<uses-permission android:name="android.permission.MANAGE_SENSORS" />
<uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
+ <uses-permission android:name="android.permission.QUERY_AUDIO_STATE" />
<uses-permission android:name="android.permission.MANAGE_CAMERA" />
<!-- Permissions needed to test system only camera devices -->
<uses-permission android:name="android.permission.CAMERA" />
@@ -426,6 +427,12 @@
<!-- Permission required for CTS test - ClipboardManagerTest -->
<uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
+ <!-- Permission required for CTS test - FontManagerTest -->
+ <uses-permission android:name="android.permission.UPDATE_FONTS" />
+
+ <!-- Permission required for hotword detection service CTS tests -->
+ <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b6fd286d3ad1..657435326d15 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -54,7 +54,7 @@ java_library {
name: "SystemUI-sensors",
srcs: [
"src/com/android/systemui/util/sensors/ThresholdSensor.java",
- ]
+ ],
}
android_library {
@@ -99,7 +99,7 @@ android_library {
"SystemUI-tags",
"SystemUI-proto",
"dagger2",
- "jsr330"
+ "jsr330",
],
manifest: "AndroidManifest.xml",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b6d942a29d49..e0097df62078 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -578,7 +578,7 @@
<!-- People Space UI Screen -->
<activity android:name=".people.PeopleSpaceActivity"
- android:label="People"
+ android:label="@string/people_tile_title"
android:enabled="true"
android:exported="true"
android:theme="@android:style/Theme.Material.NoActionBar">
@@ -592,7 +592,7 @@
<!-- People Space Widget -->
<receiver
android:name=".people.widget.PeopleSpaceWidgetProvider"
- android:label="People Space"
+ android:label="@string/people_tile_title"
android:enabled="true"
android:exported="true">
<intent-filter>
@@ -751,6 +751,17 @@
android:visibleToInstantApps="true">
</activity>
+ <activity android:name=".controls.ui.ControlsActivity"
+ android:label="@string/quick_controls_title"
+ android:theme="@style/Theme.ControlsActivity"
+ android:excludeFromRecents="true"
+ android:showWhenLocked="true"
+ android:showForAllUsers="true"
+ android:launchMode="singleInstance"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:visibleToInstantApps="true">
+ </activity>
+
<receiver android:name=".controls.management.ControlsRequestReceiver"
android:exported="true">
<intent-filter>
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 9e67e4becc8e..d6204db97872 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -25,7 +25,10 @@ java_library {
name: "SystemUIPluginLib",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "bcsmartspace/src/**/*.java",
+ ],
static_libs: [
"PluginCoreLib",
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 000000000000..f8a9a0459673
--- /dev/null
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+ int VERSION = 1;
+
+ /** Register a listener to get Smartspace data. */
+ void registerListener(SmartspaceTargetListener listener);
+
+ /** Unregister a listener. */
+ void unregisterListener(SmartspaceTargetListener listener);
+
+ /** Provides Smartspace data to registered listeners. */
+ interface SmartspaceTargetListener {
+ /** Each Parcelable is a SmartspaceTarget that represents a card. */
+ void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 4fc197340e92..1fde6c9df90a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -44,7 +44,7 @@ public interface FalsingManager {
/**
* Returns true if the FalsingManager thinks the last gesure was not a valid tap.
*
- * Accepts one parameter, robustCheck, that distinctly changes behavior. When set to false,
+ * The first parameter, robustCheck, distinctly changes behavior. When set to false,
* this method simply looks at the last gesture and returns whether it is a tap or not, (as
* opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis
* is performed that can include historical interactions and other contextual cues to see
@@ -53,8 +53,11 @@ public interface FalsingManager {
* Set robustCheck to true if you want to validate a tap for launching an action, like opening
* a notification. Set to false if you simply want to know if the last gesture looked like a
* tap.
+ *
+ * The second parameter, falsePenalty, indicates how much this should affect future gesture
+ * classifications if this tap looks like a false.
*/
- boolean isFalseTap(boolean robustCheck);
+ boolean isFalseTap(boolean robustCheck, double falsePenalty);
/**
* Returns true if the last two gestures do not look like a double tap.
@@ -92,4 +95,15 @@ public interface FalsingManager {
/** Call to report a ProximityEvent to the FalsingManager. */
void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent);
+
+ /** Adds a {@link FalsingBeliefListener}. */
+ void addFalsingBeliefListener(FalsingBeliefListener listener);
+
+ /** Removes a {@link FalsingBeliefListener}. */
+ void removeFalsingBeliefListener(FalsingBeliefListener listener);
+
+ /** Listener that is alerted when falsing belief level crosses a predfined threshold. */
+ interface FalsingBeliefListener {
+ void onFalse();
+ }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 53f7e44bc25a..ca1320448726 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,4 +50,8 @@ public abstract class QSTileView extends LinearLayout {
public abstract void onStateChanged(State state);
public abstract int getDetailY();
+
+ public View getLabelContainer() {
+ return null;
+ }
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 6ae759c66bea..50ffbc802a7d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -59,124 +59,127 @@
</com.android.keyguard.AlphaOptimizedRelativeLayout>
<LinearLayout
android:id="@+id/row1"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key1"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="1"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key2"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="2"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key3"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/pinEntry"
androidprv:digit="3"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/row2"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key4"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="4"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key5"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="5"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key6"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/pinEntry"
androidprv:digit="6"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/row3"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key7"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="7"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key8"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="8"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key9"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/pinEntry"
androidprv:digit="9"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/row4"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
>
<com.android.keyguard.NumPadButton
android:id="@+id/delete_button"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
android:contentDescription="@string/keyboardview_keycode_delete"
style="@style/NumPadKey.Delete"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key0"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pinEntry"
androidprv:digit="0"
/>
<com.android.keyguard.NumPadButton
android:id="@+id/key_enter"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
style="@style/NumPadKey.Enter"
android:contentDescription="@string/keyboardview_keycode_enter"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index f709424a7d12..297c20ef8d6d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -71,121 +71,124 @@
/>
</RelativeLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key1"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="1"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key2"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="2"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key3"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="3"
/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key4"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="4"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key5"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="5"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key6"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="6"
/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key7"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="7"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key8"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="8"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key9"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="9"
/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
>
<com.android.keyguard.NumPadButton
android:id="@+id/delete_button"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
android:contentDescription="@string/keyboardview_keycode_delete"
style="@style/NumPadKey.Delete"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key0"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/simPinEntry"
androidprv:digit="0"
/>
<com.android.keyguard.NumPadButton
android:id="@+id/key_enter"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
style="@style/NumPadKey.Enter"
android:contentDescription="@string/keyboardview_keycode_enter"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 2f9fed610f1d..388919e6d81c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -72,121 +72,125 @@
/>
</RelativeLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key1"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="1"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key2"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="2"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key3"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/pukEntry"
androidprv:digit="3"
/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
+
>
<com.android.keyguard.NumPadKey
android:id="@+id/key4"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="4"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key5"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="5"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key6"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/pukEntry"
androidprv:digit="6"
/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
>
<com.android.keyguard.NumPadKey
android:id="@+id/key7"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="7"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key8"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="8"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key9"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
androidprv:textView="@+id/pukEntry"
androidprv:digit="9"
/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
>
<com.android.keyguard.NumPadButton
android:id="@+id/delete_button"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
android:contentDescription="@string/keyboardview_keycode_delete"
style="@style/NumPadKey.Delete"
/>
<com.android.keyguard.NumPadKey
android:id="@+id/key0"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_marginEnd="@dimen/num_pad_key_margin_end"
androidprv:textView="@+id/pukEntry"
androidprv:digit="0"
/>
<com.android.keyguard.NumPadButton
android:id="@+id/key_enter"
- android:layout_width="0px"
+ android:layout_width="@dimen/num_pad_key_width"
android:layout_height="match_parent"
- android:layout_weight="1"
style="@style/NumPadKey.Enter"
android:contentDescription="@string/keyboardview_keycode_enter"
/>
diff --git a/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml
new file mode 100644
index 000000000000..be39030451f4
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <!-- Spacing around each button used for PIN view -->
+ <dimen name="num_pad_key_width">84dp</dimen>
+ <dimen name="num_pad_row_margin_bottom">12dp</dimen>
+ <dimen name="num_pad_key_margin_end">20dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 115a156b65e7..07bd2e6f0505 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -38,7 +38,7 @@
<!-- Margin around the various security views -->
<dimen name="keyguard_security_view_top_margin">8dp</dimen>
- <dimen name="keyguard_security_view_lateral_margin">36dp</dimen>
+ <dimen name="keyguard_security_view_lateral_margin">20dp</dimen>
<dimen name="keyguard_eca_top_margin">18dp</dimen>
@@ -87,5 +87,7 @@
<dimen name="disappear_y_translation">-32dp</dimen>
<!-- Spacing around each button used for PIN view -->
- <dimen name="num_pad_key_margin">2dp</dimen>
+ <dimen name="num_pad_key_width">72dp</dimen>
+ <dimen name="num_pad_row_margin_bottom">6dp</dimen>
+ <dimen name="num_pad_key_margin_end">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml
index fe2ffaa838eb..bd91ef9785d1 100644
--- a/packages/SystemUI/res/color/remote_input_send.xml
+++ b/packages/SystemUI/res/color/remote_input_send.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:color="@android:color/white" />
- <item android:color="#4dffffff" /> <!-- 30% white -->
+ <item android:state_enabled="true" android:color="?android:attr/colorAccent" />
+ <item android:color="?android:attr/colorAccent" android:alpha=".3" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
index 8e18e16f8abc..33eeb7794d09 100644
--- a/packages/SystemUI/res/color/remote_input_text.xml
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:color="@color/remote_input_text_enabled" /> <!-- white -->
- <item android:color="#99ffffff" /> <!-- 60% white -->
+ <item android:state_enabled="true" android:color="?android:attr/textColorTertiary" />
+ <item android:color="?android:attr/textColorTertiary" android:alpha=".6" />
</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml
new file mode 100644
index 000000000000..2b0ab6f9a8d2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid
+ android:color="?android:attr/colorBackground"/>
+
+ <size
+ android:width="64dp"
+ android:height="64dp"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_message.xml b/packages/SystemUI/res/drawable/ic_message.xml
new file mode 100644
index 000000000000..8219eeef4be9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_message.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:width="48dp"
+ android:height="48dp">
+ <path
+ android:pathData="M40 4H8C5.79 4 4.02 5.79 4.02 8L4 44l8 -8h28c2.21 0 4 -1.79 4 -4V8c0 -2.21 -1.79 -4 -4 -4zM12 18h24v4H12v-4zm16 10H12v-4h16v4zm8 -12H12v-4h24v4z"
+ android:fillColor="#FF000000" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_photo_camera.xml b/packages/SystemUI/res/drawable/ic_photo_camera.xml
new file mode 100644
index 000000000000..63cd4e2e5cc4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_photo_camera.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_wallet.xml b/packages/SystemUI/res/drawable/ic_qs_wallet.xml
new file mode 100644
index 000000000000..e146eabecfd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_wallet.xml
@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,
+ -0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18v-6h16v6zM20,8L4,8L4,6h16v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
new file mode 100644
index 000000000000..34675ab891c1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset">
+ <ripple
+ android:color="?android:attr/colorControlHighlight"
+ android:height="44dp">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
+ <solid android:color="@android:color/transparent"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ </ripple>
+</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml
new file mode 100644
index 000000000000..596ed711dd43
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/transparent"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ </ripple>
+</inset> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml
new file mode 100644
index 000000000000..265f575fc99c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ <item android:id="@id/background"
+ android:drawable="@drawable/qs_tile_background_shape"/>
+</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_tile_background_shape.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
new file mode 100644
index 000000000000..f6b68347124e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="@dimen/qs_corner_radius" />
+ <solid android:color="#FFFFFF" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index aed067c30253..eef37ab20c69 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -39,37 +39,34 @@
<Space android:id="@+id/space_above_icon"
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:visibility="visible" />
+ android:layout_height="48dp" />
- <!-- Use a frame layout since certain biometrics (such as UDFPS) require the icon to be centered
- within a certain area on the display. This makes it easy to 1) guarantee max size, and
- 2) center the icon within the reserved area. -->
- <FrameLayout android:id="@+id/biometric_icon_frame"
+ <FrameLayout
+ android:id="@+id/biometric_icon_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal">
+
<ImageView
android:id="@+id/biometric_icon"
android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
android:layout_gravity="center"
android:scaleType="fitXY" />
+
</FrameLayout>
- <!-- For sensors such as UDFPS, this view is used during custom measurement/layoutto add extra
+ <!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
padding so that the biometric icon is always in the right physical position. -->
<Space android:id="@+id/space_below_icon"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:visibility="gone" />
+ android:layout_height="12dp" />
<TextView
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="24dp"
- android:paddingVertical="12dp"
android:textSize="12sp"
android:gravity="center_horizontal"
android:accessibilityLiveRegion="polite"
@@ -84,6 +81,7 @@
style="?android:attr/buttonBarStyle"
android:orientation="horizontal"
android:paddingTop="16dp">
+
<Space android:id="@+id/leftSpacer"
android:layout_width="8dp"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/controls_detail_dialog.xml b/packages/SystemUI/res/layout/controls_detail_dialog.xml
index ee5315ad782f..28fc86372092 100644
--- a/packages/SystemUI/res/layout/controls_detail_dialog.xml
+++ b/packages/SystemUI/res/layout/controls_detail_dialog.xml
@@ -20,8 +20,8 @@
android:id="@+id/control_detail_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="@dimen/controls_activity_view_top_offset"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:background="@android:color/black">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -30,7 +30,7 @@
<ImageView
android:id="@+id/control_detail_close"
android:contentDescription="@string/accessibility_desc_close"
- android:src="@drawable/ic_close"
+ android:src="@drawable/ic_arrow_back"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:tint="@color/control_primary_text"
android:layout_width="48dp"
@@ -56,7 +56,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:background="@android:color/black"
android:orientation="vertical" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml
index 983999f9a5f8..1b2d2e2e9c89 100644
--- a/packages/SystemUI/res/layout/controls_in_dialog.xml
+++ b/packages/SystemUI/res/layout/controls_fullscreen.xml
@@ -20,11 +20,8 @@
android:id="@+id/control_detail_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
- android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
- android:padding="8dp"
android:orientation="vertical"
- android:background="@drawable/controls_dialog_bg">
+ android:background="@android:color/black">
<com.android.systemui.globalactions.MinHeightScrollView
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index b060afdc18e3..9d011482d011 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -25,8 +25,21 @@
<!-- make sure the header stays centered in the layout by adding a spacer -->
<Space
+ android:id="@+id/controls_spacer"
android:layout_width="@dimen/controls_header_menu_size"
- android:layout_height="1dp" />
+ android:layout_height="1dp"
+ android:visibility="gone" />
+
+ <ImageView
+ android:id="@+id/controls_close"
+ android:contentDescription="@string/accessibility_desc_close"
+ android:src="@drawable/ic_close"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:tint="@color/control_primary_text"
+ android:layout_width="@dimen/controls_header_menu_size"
+ android:layout_height="@dimen/controls_header_menu_size"
+ android:padding="12dp"
+ android:visibility="gone" />
<!-- need to keep this outer view in order to have a correctly sized anchor
for the dropdown menu, as well as dropdown background in the right place -->
<LinearLayout
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index d36c1a8c961b..42f14f30da00 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -67,7 +67,7 @@
android:textSize="15sp"
android:ellipsize="end"
android:maxLines="1"
- style="@style/TextAppearance.NotificationInfo.Title" />
+ style="@style/TextAppearance.NotificationImportanceChannel" />
<Switch
android:id="@+id/toggle"
@@ -80,7 +80,7 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
- android:background="@color/notification_channel_dialog_separator"
+ android:background="?android:attr/colorAccent"
/>
<!-- ChannelRows get added dynamically -->
@@ -90,7 +90,7 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
- android:background="@color/notification_channel_dialog_separator"
+ android:background="?android:attr/colorAccent"
/>
<RelativeLayout
android:id="@+id/bottom_actions"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index c863e02d5313..87de28650662 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -53,7 +53,7 @@
android:textSize="14sp"
android:ellipsize="end"
android:maxLines="1"
- style="@style/TextAppearance.NotificationInfo.Title"
+ style="@style/TextAppearance.NotificationImportanceChannel"
/>
<TextView
@@ -67,7 +67,7 @@
android:ellipsize="end"
android:maxLines="1"
android:layout_below="@id/channel_name"
- style="@style/TextAppearance.NotificationInfo.Secondary"
+ style="@style/TextAppearance.NotificationImportanceApp"
/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml
index 07af01b0db72..1784cae816ce 100644
--- a/packages/SystemUI/res/layout/people_space_activity.xml
+++ b/packages/SystemUI/res/layout/people_space_activity.xml
@@ -25,19 +25,30 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="vertical"
- android:padding="16dp"
+ android:padding="24dp"
android:clipChildren="false"
android:clipToPadding="false">
<TextView
+ android:id="@+id/select_conversation_title"
+ android:gravity="center"
+ android:text="@string/select_conversation_title"
+ android:layout_width="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="24sp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"/>
+
+ <TextView
android:id="@+id/select_conversation"
+ android:gravity="center"
android:text="@string/select_conversation_text"
android:layout_width="match_parent"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="24sp"
+ android:textSize="16sp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:paddingBottom="16dp" />
+ android:padding="24dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
index b1c13287d203..b1a37bf71beb 100644
--- a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
@@ -28,48 +28,27 @@
<LinearLayout
android:orientation="horizontal"
android:gravity="center"
+ android:layout_gravity="center"
android:paddingVertical="2dp"
android:paddingHorizontal="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
- android:background="@drawable/people_space_new_story_outline"
- android:id="@+id/person_icon_with_story"
- android:gravity="center_horizontal"
- android:layout_width="60dp"
- android:layout_height="60dp">
- <ImageView
- android:id="@+id/person_icon_inside_ring"
- android:layout_marginEnd="4dp"
- android:layout_marginStart="4dp"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:layout_width="52dp"
- android:layout_height="52dp"/>
- </LinearLayout>
<ImageView
- android:id="@+id/person_icon_only"
+ android:id="@+id/person_icon"
android:layout_width="60dp"
android:layout_height="60dp"/>
-
<ImageView
- android:id="@+id/package_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="-20dp"
- android:layout_marginTop="22dp"/>
-
+ android:id="@+id/availability"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp"/>
<LinearLayout
android:orientation="vertical"
- android:paddingStart="8dp"
+ android:paddingStart="4dp"
+ android:gravity="top"
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <ImageView
- android:id="@+id/availability"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp"/>
<TextView
android:id="@+id/name"
android:text="@string/empty_user_name"
diff --git a/packages/SystemUI/res/layout/people_space_notification_content_tile.xml b/packages/SystemUI/res/layout/people_space_notification_content_tile.xml
deleted file mode 100644
index 9ea7aa3c94e5..000000000000
--- a/packages/SystemUI/res/layout/people_space_notification_content_tile.xml
+++ /dev/null
@@ -1,154 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <RelativeLayout
- android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <include layout="@layout/punctuation_layout"/>
- <RelativeLayout
- android:gravity="start"
- android:id="@+id/column_one"
- android:paddingVertical="10dp"
- android:paddingStart="8dp"
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
- <TextView
- android:id="@+id/subtext"
- android:layout_toStartOf="@+id/content_layout"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- android:gravity="top|start"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="12sp"
- android:maxWidth="60dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="1"
- android:ellipsize="end"/>
- <LinearLayout
- android:orientation="horizontal"
- android:id="@+id/avatar_and_app_icon"
- android:layout_alignParentBottom="true"
- android:layout_alignParentStart="true"
- android:gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <LinearLayout
- android:id="@+id/person_icon_with_story"
- android:background="@drawable/people_space_new_story_outline"
- android:gravity="center_horizontal"
- android:layout_width="48dp"
- android:layout_height="48dp">
- <ImageView
- android:id="@+id/person_icon_inside_ring"
- android:layout_marginEnd="4dp"
- android:layout_marginStart="4dp"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:layout_width="40dp"
- android:layout_height="40dp"/>
- </LinearLayout>
- <ImageView
- android:id="@+id/person_icon_only"
- android:layout_width="48dp"
- android:layout_height="48dp"/>
- <ImageView
- android:id="@id/package_icon"
- android:layout_marginStart="-16dp"
- android:layout_marginTop="18dp"
- android:paddingBottom="10dp"
- android:paddingEnd="8dp"
- android:layout_width="28dp"
- android:layout_height="32dp"/>
- </LinearLayout>
- </RelativeLayout>
- <LinearLayout
- android:id="@+id/content_layout"
- android:paddingBottom="4dp"
- android:paddingStart="4dp"
- android:paddingTop="8dp"
- android:paddingEnd="8dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_toEndOf="@id/column_one"
- android:gravity="top|end"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <TextView
- android:id="@+id/content"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:gravity="top|end"
- android:textSize="12sp"
- android:paddingTop="2dp"
- android:paddingEnd="4dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:ellipsize="end"/>
- <LinearLayout
- android:id="@+id/content_background"
- android:background="@drawable/people_space_content_background"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:id="@+id/image"
- android:adjustViewBounds="true"
- android:maxHeight="44dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scaleType="centerCrop"/>
- </LinearLayout>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/person_label"
- android:paddingBottom="10dp"
- android:paddingEnd="8dp"
- android:gravity="start|bottom"
- android:layout_toEndOf="@id/column_one"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/content_layout"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:id="@+id/availability"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:paddingVertical="2dp"
- android:background="@drawable/circle_green_10dp"/>
- <TextView
- android:id="@+id/name"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:maxLines="1"
- android:ellipsize="end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- </RelativeLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
new file mode 100644
index 000000000000..3ced1ff6c74b
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
@@ -0,0 +1,75 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout
+ android:background="@drawable/people_space_tile_view_card"
+ android:id="@+id/item"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:paddingVertical="2dp"
+ android:paddingHorizontal="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:background="@drawable/ic_person"
+ android:id="@+id/person_icon_only"
+ android:layout_width="60dp"
+ android:layout_height="60dp"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:paddingStart="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp"/>
+ <TextView
+ android:id="@+id/name"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/last_interaction"
+ android:text="@string/empty_status"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="12sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="3"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
index 33004952d947..6a606e10e3fb 100644
--- a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
@@ -1,44 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
- <RelativeLayout
+ <LinearLayout
android:background="@drawable/people_space_tile_view_card"
android:id="@+id/item"
+ android:orientation="vertical"
+ android:layout_gravity="center"
+ android:padding="8dp"
android:layout_width="match_parent"
- android:layout_height="match_parent">
- <RelativeLayout
- android:gravity="start"
- android:id="@+id/column_one"
- android:paddingVertical="8dp"
- android:paddingStart="8dp"
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:gravity="top"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:gravity="start"
+ android:id="@+id/person_icon"
+ android:layout_width="56dp"
+ android:layout_height="56dp"/>
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:gravity="top|start"
+ android:paddingStart="12dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/subtext"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp"
+ android:paddingBottom="4dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"/>
<LinearLayout
- android:orientation="horizontal"
android:id="@+id/content_background"
android:background="@drawable/people_space_content_background"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- android:layout_width="60dp"
- android:layout_height="60dp">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:gravity="center"
@@ -46,105 +71,33 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
- <ImageView
- android:id="@+id/status_defined_icon"
- android:gravity="start|top"
- android:layout_marginStart="-52dp"
- android:layout_marginTop="8dp"
- android:layout_width="18dp"
- android:layout_height="18dp"/>
</LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:id="@+id/avatar_and_app_icon"
- android:layout_alignParentBottom="true"
- android:layout_alignParentStart="true"
- android:paddingStart="4dp"
- android:gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <LinearLayout
- android:id="@+id/person_icon_with_story"
- android:background="@drawable/people_space_new_story_outline"
- android:gravity="center_horizontal"
- android:layout_width="48dp"
- android:layout_height="48dp">
- <ImageView
- android:id="@+id/person_icon_inside_ring"
- android:layout_marginEnd="4dp"
- android:layout_marginStart="4dp"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:layout_width="40dp"
- android:layout_height="40dp"/>
- </LinearLayout>
- <ImageView
- android:id="@+id/person_icon_only"
- android:layout_width="48dp"
- android:layout_height="48dp"/>
- <ImageView
- android:id="@id/package_icon"
- android:layout_marginStart="-16dp"
- android:layout_marginTop="18dp"
- android:paddingBottom="10dp"
- android:paddingEnd="8dp"
- android:layout_width="28dp"
- android:layout_height="32dp"/>
- </LinearLayout>
- </RelativeLayout>
- <LinearLayout
- android:id="@+id/content_layout"
- android:paddingTop="10dp"
- android:paddingBottom="4dp"
- android:paddingHorizontal="8dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_toEndOf="@id/column_one"
- android:gravity="top|end"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
<TextView
- android:id="@+id/status"
+ android:id="@+id/text_content"
+ android:text="@string/empty_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
android:textSize="12sp"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:singleLine="false"
android:ellipsize="end"/>
+ </LinearLayout>
</LinearLayout>
<LinearLayout
- android:id="@+id/person_label"
- android:paddingBottom="10dp"
- android:paddingEnd="8dp"
- android:gravity="start|bottom"
- android:layout_toEndOf="@id/column_one"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/content_layout"
- android:orientation="vertical"
+ android:gravity="bottom"
+ android:orientation="horizontal"
+ android:paddingTop="4dp"
+ android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <TextView
- android:id="@+id/time"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="12sp"
- android:paddingVertical="2dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="1"
- android:visibility="gone"
- android:ellipsize="end"/>
- <ImageView
- android:id="@+id/availability"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:paddingVertical="2dp"
- android:background="@drawable/circle_green_10dp"/>
+
<TextView
android:id="@+id/name"
+ android:gravity="center_vertical"
+ android:layout_weight="1"
+ android:text="@string/empty_user_name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"
@@ -152,6 +105,13 @@
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:gravity="end"
+ android:paddingStart="6dp"
+ android:layout_width="24dp"
+ android:layout_height="18dp"/>
</LinearLayout>
- </RelativeLayout>
-</LinearLayout> \ No newline at end of file
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/packages/SystemUI/res/layout/people_space_small_view.xml b/packages/SystemUI/res/layout/people_space_small_view.xml
new file mode 100644
index 000000000000..7b1abaed28ca
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_small_view.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@drawable/people_space_tile_view_card"
+ android:orientation="vertical"
+ android:paddingHorizontal="4dp"
+ android:paddingVertical="8dp">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:layout_gravity="center"
+ android:paddingTop="4dp"
+ android:layout_width="18dp"
+ android:layout_height="22dp"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_gravity="center"
+ android:paddingTop="4dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:paddingHorizontal="8dp"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp" />
+ </LinearLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index a5b8cfa5d60b..c521dc2b45a5 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -28,13 +28,6 @@
android:clipToPadding="false"
android:focusable="true" >
- <include
- layout="@layout/mobile_signal_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_carrier_margin_width"
- android:visibility="gone" />
-
<com.android.systemui.util.AutoMarqueeTextView
android:id="@+id/qs_carrier_text"
android:layout_width="wrap_content"
@@ -46,4 +39,11 @@
android:singleLine="true"
android:maxEms="7"/>
+ <include
+ layout="@layout/mobile_signal_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_carrier_margin_width"
+ android:visibility="gone" />
+
</com.android.systemui.qs.carrier.QSCarrier> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 93dd1a12f147..02179722b35c 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -89,7 +89,7 @@
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_lock_power_off"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml b/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml
new file mode 100644
index 000000000000..726e69f6e50c
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+-->
+
+<!-- Extends FrameLayout -->
+<com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/qs_footer"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_height"
+ android:layout_marginStart="@dimen/qs_footer_margin"
+ android:layout_marginEnd="@dimen/qs_footer_margin"
+ android:background="@android:color/transparent"
+ android:baselineAligned="false"
+ android:clickable="false"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/build"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/qs_tile_margin_horizontal"
+ android:paddingEnd="4dp"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ android:visibility="gone" />
+
+ <com.android.systemui.qs.PageIndicator
+ android:id="@+id/footer_page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone" />
+
+ <View
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qs_footer_actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_mode_edit"
+ android:tint="?android:attr/colorForeground" />
+
+ <com.android.systemui.statusbar.phone.MultiUserSwitch
+ android:id="@+id/multi_user_switch"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:focusable="true">
+
+ <ImageView
+ android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_expanded_size"
+ android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside" />
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/settings_button_container"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <com.android.systemui.statusbar.phone.SettingsButton
+ android:id="@+id/settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_quick_settings_settings"
+ android:background="@drawable/qs_footer_action_chip_background_borderless"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/colorForeground" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/tuner_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="36dp"
+ android:paddingEnd="4dp"
+ android:src="@drawable/tuner"
+ android:tint="?android:attr/textColorTertiary"
+ android:visibility="invisible" />
+
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/pm_lite"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_lock_power_off"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
+ android:tint="?android:attr/colorForeground" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+</com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 77f17439c487..d29cf872fd26 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -42,7 +42,10 @@
android:background="@android:color/transparent"
android:focusable="true"
android:accessibilityTraversalBefore="@android:id/edit">
- <include layout="@layout/qs_footer_impl" />
+ <ViewStub
+ android:id="@+id/qs_footer_stub"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
<include layout="@layout/qs_media_divider"
android:id="@+id/divider"/>
</com.android.systemui.qs.QSPanel>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
index b969a1527c09..4798b437298a 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -38,7 +38,6 @@
android:id="@+id/device_management_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/monitoring_title_device_owned"
style="@style/DeviceManagementDialogTitle"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index b5d48b4636a8..43182eb991e5 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -19,7 +19,6 @@
<!-- LinearLayout -->
<com.android.systemui.statusbar.policy.RemoteInputView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/systemui_theme_remote_input"
android:id="@+id/remote_input"
android:layout_height="match_parent"
android:layout_width="match_parent">
@@ -33,6 +32,10 @@
android:paddingBottom="4dp"
android:paddingStart="16dp"
android:paddingEnd="12dp"
+ android:layout_marginRight="5dp"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginBottom="20dp"
android:gravity="start|center_vertical"
android:textAppearance="?android:attr/textAppearance"
android:textColor="@color/remote_input_text"
@@ -53,6 +56,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
+ android:paddingBottom="20dp"
android:paddingStart="12dp"
android:paddingEnd="24dp"
android:id="@+id/remote_input_send"
@@ -66,6 +70,7 @@
android:id="@+id/remote_input_progress"
android:layout_width="24dp"
android:layout_height="24dp"
+ android:layout_marginBottom="10dp"
android:layout_marginEnd="6dp"
android:layout_gravity="center"
android:visibility="invisible"
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_bp_view.xml
index 0cfbf2e61dd1..f1c55ef16cdc 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
+++ b/packages/SystemUI/res/layout/udfps_bp_view.xml
@@ -14,9 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewBp
+<com.android.systemui.biometrics.UdfpsBpView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewBp>
+</com.android.systemui.biometrics.UdfpsBpView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_enroll_view.xml
index 9b5752d2de59..40353052ca85 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
+++ b/packages/SystemUI/res/layout/udfps_enroll_view.xml
@@ -14,13 +14,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewEnroll
+<com.android.systemui.biometrics.UdfpsEnrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- Enrollment progress bar-->
+ <!-- Enrollment progress bar -->
<com.android.systemui.biometrics.UdfpsProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
@@ -31,4 +31,9 @@
android:layout_gravity="center"
android:visibility="gone"/>
-</com.android.systemui.biometrics.UdfpsAnimationViewEnroll>
+ <!-- Fingerprint -->
+ <ImageView
+ android:id="@+id/udfps_enroll_animation_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsEnrollView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
index 644d1adac46b..6ecbb473d720 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
@@ -14,9 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
+<com.android.systemui.biometrics.UdfpsFpmOtherView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard>
+
+ <!-- Fingerprint -->
+ <ImageView
+ android:id="@+id/udfps_fpm_other_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsFpmOtherView>
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
new file mode 100644
index 000000000000..562040b0ad82
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsKeyguardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Background protection -->
+ <ImageView
+ android:id="@+id/udfps_keyguard_fp_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/fingerprint_bg"
+ android:visibility="gone"/>
+
+ <!-- Fingerprint -->
+ <ImageView
+ android:id="@+id/udfps_keyguard_animation_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsKeyguardView>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index e24c9e99a405..50b2f209d871 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -22,6 +22,11 @@
android:layout_height="match_parent"
systemui:sensorTouchAreaCoefficient="0.5">
+ <ViewStub
+ android:id="@+id/animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
<com.android.systemui.biometrics.UdfpsSurfaceView
android:id="@+id/hbm_view"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c36f7cd630a9..55365bd3c692 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,6 +36,7 @@
<color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
<color name="qs_customize_decoration">@color/GM2_grey_300</color>
+ <color name="qs_footer_action_border">#2E312C</color>
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
@@ -114,8 +115,6 @@
<color name="notification_section_header_label_color">@color/GM2_grey_900</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
- <!-- The divider view for the notification channel editor half-shelf -->
- <color name="notification_channel_dialog_separator">@color/GM2_grey_200</color>
<color name="assist_orb_color">#ffffff</color>
@@ -152,10 +151,9 @@
<color name="minimize_dock_shadow_end">#00000000</color>
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
- <color name="remote_input_text_enabled">#ffffffff</color>
<color name="remote_input_hint">#99ffffff</color>
- <color name="remote_input_accent">#eeeeee</color>
+ <color name="remote_input_accent">?android:attr/colorAccent</color>
<color name="quick_step_track_background_background_dark">#1F000000</color>
<color name="quick_step_track_background_background_light">#33FFFFFF</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b75a0bc1525a..af6df32a02b0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet
</string>
<!-- The tiles to display in QuickSettings -->
@@ -180,6 +180,9 @@
<!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
<integer name="doze_pickup_vibration_threshold">2000</integer>
+ <!-- Doze: quick pickup duration to stay in AOD until the next gesture is triggered -->
+ <integer name="doze_quick_pickup_aod_duration">5000</integer>
+
<!-- Type of a sensor that provides a low-power estimate of the desired display
brightness, suitable to listen to while the device is asleep (e.g. during
always-on display) -->
@@ -577,9 +580,15 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
+ <!-- Package name of the preferred system app to perform eSOS action -->
+ <string name="config_preferredEmergencySosPackage" translatable="false"></string>
+
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">false</bool>
<!-- Determines whether the shell features all run on another thread. -->
<bool name="config_enableShellMainThread">false</bool>
+
+ <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
+ <bool name="allow_force_nav_bar_handle_opaque">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 392eb496031a..2062104be2df 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -167,15 +167,11 @@
and the notification won't use this much, but is measured with wrap_content -->
<dimen name="notification_messaging_actions_min_height">196dp</dimen>
- <!-- Height of a call notification. Note that this is an upper bound
- and the notification won't use this much, but is measured with wrap_content -->
- <dimen name="call_notification_full_height">172dp</dimen>
-
<!-- a threshold in dp per second that is considered fast scrolling -->
<dimen name="scroll_fast_threshold">1500dp</dimen>
<!-- Height of a the shelf with the notification icons -->
- <dimen name="notification_shelf_height">32dp</dimen>
+ <dimen name="notification_shelf_height">48dp</dimen>
<!-- Minimum height of a notification to be interactable -->
<dimen name="notification_min_interaction_height">40dp</dimen>
@@ -415,6 +411,9 @@
<!-- The size of each of the icon buttons in the QS footer -->
<dimen name="qs_footer_action_button_size">@dimen/qs_footer_height</dimen>
+ <!-- (48dp - 44dp) / 2 -->
+ <dimen name="qs_footer_action_inset">2dp</dimen>
+
<!-- Margins on each side of QS Footer -->
<dimen name="qs_footer_margin">2dp</dimen>
@@ -513,6 +512,7 @@
<!-- The size of the gesture span needed to activate the "pull" notification expansion -->
<dimen name="pull_span_min">25dp</dimen>
+ <dimen name="qs_corner_radius">14dp</dimen>
<dimen name="qs_tile_height">96dp</dimen>
<!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
<dimen name="qs_tile_layout_margin_side">18dp</dimen>
@@ -529,6 +529,8 @@
<dimen name="qs_tile_margin_top">0dp</dimen>
<dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
<dimen name="qs_tile_background_size">44dp</dimen>
+ <dimen name="qs_icon_size">20dp</dimen>
+ <dimen name="qs_label_container_margin">10dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index e5518928c98c..2163806666d9 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -38,6 +38,8 @@
<!-- People Tile flag -->
<bool name="flag_conversations">false</bool>
+ <bool name="flag_wallet">false</bool>
+
<!-- The new animations to/from lockscreen and AOD! -->
<bool name="flag_lockscreen_animations">false</bool>
@@ -46,4 +48,6 @@
<bool name="flag_navigation_bar_overlay">false</bool>
<bool name="flag_pm_lite">false</bool>
+
+ <bool name="flag_alarm_tile">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 783f80c2ee02..78180a7a80a8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -851,8 +851,6 @@
<string name="quick_settings_wifi_label">Wi-Fi</string>
<!-- QuickSettings: Internet [CHAR LIMIT=NONE] -->
<string name="quick_settings_internet_label">Internet</string>
- <!-- QuickSettings: Airplane-safe [CHAR LIMIT=NONE] -->
- <string name="quick_settings_airplane_safe_label">Airplane-safe</string>
<!-- QuickSettings: networks available [CHAR LIMIT=NONE] -->
<string name="quick_settings_networks_available">Networks available</string>
<!-- QuickSettings: networks unavailable [CHAR LIMIT=NONE] -->
@@ -1259,6 +1257,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the organization can monitor network traffic on that device. The placeholder is the organization's name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_management_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> owns this device and may monitor network traffic</string>
+ <!-- Disclosure at the bottom of Quick Settings that indicates that the user's financed device belongs to the Creditor. The placeholder is the Creditor's name. [CHAR LIMIT=100] -->
+ <string name="quick_settings_financed_disclosure_named_management">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
+
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
@@ -1298,6 +1299,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_vpn">This device is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+ <!-- Monitoring dialog title for financed device [CHAR LIMIT=60] -->
+ <string name="monitoring_title_financed_device">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
+
<!-- Monitoring dialog title for device owned devices [CHAR LIMIT=35] -->
<string name="monitoring_title_device_owned">Device management</string>
@@ -1332,6 +1336,9 @@
<!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
+ <!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device, manage apps, and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string>
+
<!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
@@ -1614,6 +1621,12 @@
<!-- Name of the alarm status bar icon. -->
<string name="status_bar_alarm">Alarm</string>
+ <!-- Wallet strings -->
+ <!-- Wallet empty state, title [CHAR LIMIT=32] -->
+ <string name="wallet_title">Wallet</string>
+ <!-- Secondary label of the quick access wallet tile. [CHAR LIMIT=32] -->
+ <string name="wallet_secondary_label">Ready</string>
+
<!-- Name of the work status bar icon. -->
<string name="status_bar_work">Work profile</string>
@@ -2297,6 +2310,9 @@
<!-- accessibility label for button to select user [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_user">Signed in as <xliff:g name="user" example="John">%s</xliff:g></string>
+ <!-- Accessibility description (not shown on the screen) of action to open user switcher when the multi-user icon is clicked. It will read as "Double-tap to choose user" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_choose_user_action">choose user</string>
+
<!-- Content description for no internet connection [CHAR LIMIT=NONE] -->
<string name="data_connection_no_internet">No internet</string>
@@ -2312,6 +2328,9 @@
<!-- accessibility label for button to edit quick settings [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_edit">Edit order of settings.</string>
+ <!-- accessibility label for button to open power menu [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_power_menu">Power menu</string>
+
<!-- accessibility label for paging indicator in quick settings [CHAR LIMITi=NONE] -->
<string name="accessibility_quick_settings_page">Page <xliff:g name="current_page" example="1">%1$d</xliff:g> of <xliff:g name="num_pages" example="2">%2$d</xliff:g></string>
@@ -2648,7 +2667,7 @@
<!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_description">Magnification switch</string>
<!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
- <string name="magnification_mode_switch_state_full_screen">Magnify entire screen</string>
+ <string name="magnification_mode_switch_state_full_screen">Magnify full screen</string>
<!-- A11y state description for magnification mode switch that device is in window mode. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_state_window">Magnify part of screen</string>
<!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
@@ -2808,44 +2827,44 @@
<!-- Text to display when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_copy_toast">Build number copied to clipboard.</string>
- <!-- Status for last interaction with exact time [CHAR LIMIT=120] -->
- <string name="last_interaction_status" translatable="false">Last chatted <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
- <!-- Status for last interaction when less than a certain time window [CHAR LIMIT=120] -->
- <string name="last_interaction_status_less_than" translatable="false">Last chatted less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
- <!-- Status for last interaction when over a certain time window [CHAR LIMIT=120] -->
- <string name="last_interaction_status_over" translatable="false">Last chatted over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
- <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
- <string name="basic_status" translatable="false">Open conversation</string>
- <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
- <string name="select_conversation_text" translatable="false">Select one conversation to show in your widget:</string>
- <!-- Timestamp for notification with exact time [CHAR LIMIT=120] -->
- <string name="timestamp" translatable="false"><xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
- <!-- Timestamp for notification when less than a certain time window [CHAR LIMIT=120] -->
- <string name="less_than_timestamp" translatable="false">Less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
- <!-- Timestamp for notification when over a certain time window [CHAR LIMIT=120] -->
- <string name="over_timestamp" translatable="false">Over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
- <!-- Status text for a birthday today [CHAR LIMIT=30] -->
- <string name="birthday_status" translatable="false">Birthday</string>
- <!-- Status text for an upcoming birthday [CHAR LIMIT=30] -->
- <string name="upcoming_birthday_status" translatable="false">Birthday soon</string>
- <!-- Status text for an anniversary [CHAR LIMIT=30] -->
- <string name="anniversary_status" translatable="false">Anniversary</string>
- <!-- Status text for sharing location [CHAR LIMIT=30] -->
- <string name="location_status" translatable="false">Sharing location</string>
- <!-- Status text for a new story posted [CHAR LIMIT=30] -->
- <string name="new_story_status" translatable="false">New story</string>
- <!-- Status text for watching a video [CHAR LIMIT=30] -->
- <string name="video_status" translatable="false">Watching</string>
- <!-- Status text for listening to audio [CHAR LIMIT=30] -->
- <string name="audio_status" translatable="false">Listening</string>
- <!-- Status text for playing a game [CHAR LIMIT=30] -->
- <string name="game_status" translatable="false">Playing</string>
- <!-- Empty user name before user has selected a friend [CHAR LIMIT=30] -->
- <string name="empty_user_name" translatable="false">Your friend</string>
- <!-- Empty status shown before user has selected a friend [CHAR LIMIT=30] -->
- <string name="empty_status" translatable="false">Their status</string>
- <!-- Default text for missed call notifications [CHAR LIMIT=30] -->
- <string name="missed_call" translatable="false">Missed call</string>
+ <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
+ <string name="basic_status">Open conversation</string>
+ <!--Title text for Conversation widget set up screen [CHAR LIMIT=180] -->
+ <string name="select_conversation_title">Conversation widgets</string>
+ <!--Text explaining to tap a conversation to select it show in their Conversation widget [CHAR LIMIT=180] -->
+ <string name="select_conversation_text">Tap a conversation to add it to your Home screen</string>
+ <!-- Timestamp for notification with exact time [CHAR LIMIT=25] -->
+ <string name="timestamp"><xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
+ <!-- Timestamp for notification when less than a certain time window [CHAR LIMIT=25] -->
+ <string name="less_than_timestamp">Less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
+ <!-- Timestamp for notification when over a certain time window [CHAR LIMIT=25] -->
+ <string name="over_timestamp">Over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
+ <!-- Status text on the Conversation widget for a birthday today [CHAR LIMIT=20] -->
+ <string name="birthday_status">Birthday</string>
+ <!-- Status text on the Conversation widget for an upcoming birthday [CHAR LIMIT=20] -->
+ <string name="upcoming_birthday_status">Birthday soon</string>
+ <!-- Status text on the Conversation widget for an anniversary [CHAR LIMIT=20] -->
+ <string name="anniversary_status">Anniversary</string>
+ <!-- Status text on the Conversation widget for sharing location [CHAR LIMIT=20] -->
+ <string name="location_status">Sharing location</string>
+ <!-- Status text on the Conversation widget for a new story posted [CHAR LIMIT=20] -->
+ <string name="new_story_status">New story</string>
+ <!-- Status text on the Conversation widget for watching a video [CHAR LIMIT=20] -->
+ <string name="video_status">Watching</string>
+ <!-- Status text on the Conversation widget for listening to audio [CHAR LIMIT=20] -->
+ <string name="audio_status">Listening</string>
+ <!-- Status text on the Conversation widget for playing a game [CHAR LIMIT=20] -->
+ <string name="game_status">Playing</string>
+ <!-- Empty user name before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
+ <string name="empty_user_name">Friends</string>
+ <!-- Empty status shown before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
+ <string name="empty_status">Let’s chat tonight!</string>
+ <!-- Default text for missed call notifications on their Conversation widget [CHAR LIMIT=20] -->
+ <string name="missed_call">Missed call</string>
+ <!-- Description text for adding a Conversation widget [CHAR LIMIT=100] -->
+ <string name="people_tile_description">See recent messages, missed calls, and status updates</string>
+ <!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
+ <string name="people_tile_title">Conversation</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
@@ -2858,4 +2877,7 @@
<string name="qs_remove_labels" translatable="false"></string>
<string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamily</string>
+
+ <!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] -->
+ <string name="qs_alarm_tile_no_alarm">No alarm set</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ec26b8d7a6a8..ff9ea0175ec0 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -244,6 +244,8 @@
<item name="android:paddingTop">12dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">24sp</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle">
@@ -251,6 +253,8 @@
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">16sp</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Description">
@@ -258,6 +262,8 @@
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Error">
@@ -378,10 +384,6 @@
<!-- Overridden by values-television/styles.xml with tv-specific settings -->
<style name="volume_dialog_theme" parent="qs_theme"/>
- <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
- <item name="android:colorAccent">@color/remote_input_accent</item>
- </style>
-
<style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog" />
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -643,6 +645,16 @@
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
+ <style name="Theme.ControlsActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+ <item name="android:windowActivityTransitions">true</item>
+ <item name="android:windowContentTransitions">false</item>
+ <item name="android:windowIsTranslucent">false</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:statusBarColor">@*android:color/transparent</item>
+ <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
+ </style>
+
<style name="Theme.CreateUser" parent="@style/Theme.SystemUI">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#33000000</item>
@@ -654,30 +666,12 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
- <style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
- <item name="android:windowAnimationStyle">@style/Animation.Bottomsheet</item>
- <item name="android:windowFullscreen">true</item>
- <item name="android:windowIsFloating">false</item>
- <item name="android:windowBackground">@null</item>
- <item name="android:backgroundDimEnabled">true</item>
- </style>
-
- <style name="Animation.Bottomsheet">
- <item name="android:windowEnterAnimation">@anim/bottomsheet_in</item>
- <item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
- </style>
-
- <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
- <item name="android:windowAnimationStyle">@style/Animation.ControlDialog</item>
- <item name="android:windowFullscreen">true</item>
+ <style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="android:windowFullscreen">false</item>
<item name="android:windowIsFloating">false</item>
- <item name="android:windowBackground">@null</item>
- <item name="android:backgroundDimEnabled">true</item>
- </style>
-
- <style name="Animation.ControlDialog">
- <item name="android:windowEnterAnimation">@*android:anim/dialog_enter</item>
- <item name="android:windowExitAnimation">@*android:anim/dialog_exit</item>
+ <item name="android:windowBackground">@android:color/black</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
<style name="Control" />
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index fbdac5e1789b..d2bff180ef8a 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -15,13 +15,15 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="140dp"
- android:minHeight="55dp"
- android:minResizeWidth="110dp"
- android:minResizeHeight="55dp"
+ android:minWidth="120dp"
+ android:minHeight="54dp"
+ android:minResizeWidth="60dp"
+ android:minResizeHeight="54dp"
+ android:maxResizeHeight="207dp"
android:updatePeriodMillis="60000"
- android:previewImage="@drawable/ic_person"
+ android:description="@string/people_tile_description"
+ android:previewLayout="@layout/people_space_placeholder_layout"
android:resizeMode="horizontal|vertical"
android:configure="com.android.systemui.people.PeopleSpaceActivity"
- android:initialLayout="@layout/people_space_widget">
+ android:initialLayout="@layout/people_space_placeholder_layout">
</appwidget-provider>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 72e4061829fa..f50c3c925a1c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -20,7 +20,6 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.Choreographer;
-import android.view.Surface;
import android.view.SurfaceControl;
/**
@@ -91,24 +90,6 @@ public class PipSurfaceTransactionHelper {
.setPosition(leash, positionX, positionY);
}
- public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds,
- @Surface.Rotation int rotation) {
- resetScale(tx, leash, destinationBounds);
- if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
- final int degree = (rotation == Surface.ROTATION_90) ? -90 : 90;
- mTmpTransform.setRotate(degree, 0, 0);
- tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
- }
- resetCornerRadius(tx, leash);
- crop(tx, leash, destinationBounds);
- }
-
- public void resetScale(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect destinationBounds) {
- tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9)
- .setPosition(leash, destinationBounds.left, destinationBounds.top);
- }
-
public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) {
tx.setCornerRadius(leash, 0);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 49e86f55bb9e..512628447530 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -63,15 +63,9 @@ interface ISystemUiProxy {
Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
/**
- * Control the {@param alpha} of the back button in the navigation bar and {@param animate} if
- * needed from current value
- * @deprecated
- */
- void setBackButtonAlpha(float alpha, boolean animate) = 8;
-
- /**
* Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
- * and home bar in no-button mode) and {@param animate} if needed from current value
+ * and home handle & background in gestural mode). The {@param animate} is currently only
+ * supported for 2 button mode.
*/
void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index ebb6e30d4b3b..e9e9b2421d4a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -289,15 +289,19 @@ public class Task {
/**
* Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
*/
- public float getVisibleThumbnailRatio() {
+ public float getVisibleThumbnailRatio(boolean clipInsets) {
if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
return 0f;
}
- float availableWidth = lastSnapshotData.taskSize.x - (lastSnapshotData.contentInsets.left
- + lastSnapshotData.contentInsets.right);
- float availableHeight = lastSnapshotData.taskSize.y - (lastSnapshotData.contentInsets.top
- + lastSnapshotData.contentInsets.bottom);
+ float availableWidth = lastSnapshotData.taskSize.x;
+ float availableHeight = lastSnapshotData.taskSize.y;
+ if (clipInsets) {
+ availableWidth -=
+ (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right);
+ availableHeight -=
+ (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom);
+ }
return availableWidth / availableHeight;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index af7c5da7b878..bf0d29a59bea 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -16,11 +16,11 @@
package com.android.systemui.shared.system;
-import android.window.TaskSnapshot;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
+import android.window.TaskSnapshot;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -76,10 +76,14 @@ public class RecentsAnimationControllerCompat {
* accordingly. This should be called before `finish`
* @param taskId Task id of the Activity in PiP mode.
* @param destinationBounds Bounds of the PiP window on home.
+ * @param windowCrop bounds to crop as part of final transform.
+ * @param float9 An array of 9 floats to be used as matrix transform.
*/
- public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
+ float[] float9) {
try {
- mAnimationController.setFinishTaskBounds(taskId, destinationBounds);
+ mAnimationController.setFinishTaskBounds(taskId, destinationBounds, windowCrop,
+ float9);
} catch (RemoteException e) {
Log.d(TAG, "Failed to set finish task bounds", e);
}
@@ -141,9 +145,9 @@ public class RecentsAnimationControllerCompat {
/**
* @see IRecentsAnimationController#detachNavigationBarFromApp
*/
- public void detachNavigationBarFromApp() {
+ public void detachNavigationBarFromApp(boolean moveHomeToTop) {
try {
- mAnimationController.detachNavigationBarFromApp();
+ mAnimationController.detachNavigationBarFromApp(moveHomeToTop);
} catch (RemoteException e) {
Log.e(TAG, "Failed to detach the navigation bar from app", e);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 7ba906986fa3..06155bc7100c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -137,8 +137,11 @@ public class RemoteTransitionCompat implements Parcelable {
mWrapped.hideCurrentInputMethod();
}
- @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
- if (mWrapped != null) mWrapped.setFinishTaskBounds(taskId, destinationBounds);
+ @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds,
+ Rect windowCrop, float[] float9) {
+ if (mWrapped != null) {
+ mWrapped.setFinishTaskBounds(taskId, destinationBounds, windowCrop, float9);
+ }
}
@Override public void finish(boolean toHome, boolean sendUserLeaveHint) {
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 1569fff63453..3f0e3eb84424 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -45,6 +45,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
private int mLockScreenColor;
private boolean mIsDozing;
+ private float mDozeAmount;
private Locale mLocale;
private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
@@ -59,6 +60,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
super(view);
mStatusBarStateController = statusBarStateController;
mIsDozing = mStatusBarStateController.isDozing();
+ mDozeAmount = mStatusBarStateController.getDozeAmount();
mBroadcastDispatcher = broadcastDispatcher;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
@@ -82,6 +84,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
mStatusBarStateController.addCallback(mStatusBarStateListener);
mIsDozing = mStatusBarStateController.isDozing();
+ mDozeAmount = mStatusBarStateController.getDozeAmount();
refreshTime();
initColors();
}
@@ -136,9 +139,15 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
- public void onDozingChanged(boolean isDozing) {
- mIsDozing = isDozing;
- mView.animateDoze(mIsDozing, true);
+ public void onDozeAmountChanged(float linear, float eased) {
+ boolean noAnimation = (mDozeAmount == 0f && linear == 1f)
+ || (mDozeAmount == 1f && linear == 0f);
+ boolean isDozing = linear > mDozeAmount;
+ mDozeAmount = linear;
+ if (mIsDozing != isDozing) {
+ mIsDozing = isDozing;
+ mView.animateDoze(mIsDozing, !noAnimation);
+ }
}
};
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 5760565aaab1..9f32c03d1de4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -35,12 +35,15 @@ import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingClassifier;
+import com.android.systemui.classifier.FalsingCollector;
public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
extends KeyguardInputViewController<T> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
private final LatencyTracker mLatencyTracker;
+ private final FalsingCollector mFalsingCollector;
private CountDownTimer mCountdownTimer;
protected KeyguardMessageAreaController mMessageAreaController;
private boolean mDismissing;
@@ -70,11 +73,12 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker) {
+ LatencyTracker latencyTracker, FalsingCollector falsingCollector) {
super(view, securityMode, keyguardSecurityCallback);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
+ mFalsingCollector = falsingCollector;
KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
mMessageAreaController = messageAreaControllerFactory.create(kma);
}
@@ -181,8 +185,8 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
if (timeoutMs == 0) {
mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
}
+ mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
}
- mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
}
protected void verifyPasswordAndUnlock() {
@@ -256,6 +260,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
}
protected void onUserInput() {
+ mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.passed(0.6));
getKeyguardSecurityCallback().userActivity();
getKeyguardSecurityCallback().onUserInput();
mMessageAreaController.setMessage("");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 5e02e0440c3d..40190c18935b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -26,16 +26,12 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.systemui.Gefingerpoken;
-
-import java.util.ArrayList;
-import java.util.List;
/**
* A Base class for all Keyguard password/pattern/pin related inputs.
*/
public abstract class KeyguardInputView extends LinearLayout {
- private final List<Gefingerpoken> mMotionEventListener = new ArrayList<>();
+ private Runnable mOnFinishImeAnimationRunnable;
public KeyguardInputView(Context context) {
super(context);
@@ -52,7 +48,7 @@ public abstract class KeyguardInputView extends LinearLayout {
abstract CharSequence getTitle();
- void animateForIme(float interpolatedFraction) {
+ void animateForIme(float interpolatedFraction, boolean appearingAnim) {
return;
}
@@ -66,27 +62,6 @@ public abstract class KeyguardInputView extends LinearLayout {
return false;
}
- void addMotionEventListener(Gefingerpoken listener) {
- mMotionEventListener.add(listener);
- }
-
- void removeMotionEventListener(Gefingerpoken listener) {
- mMotionEventListener.remove(listener);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mMotionEventListener.stream().anyMatch(listener -> listener.onTouchEvent(event))
- || super.onTouchEvent(event);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mMotionEventListener.stream().anyMatch(
- listener -> listener.onInterceptTouchEvent(event))
- || super.onInterceptTouchEvent(event);
- }
-
protected AnimatorListenerAdapter getAnimationListener(int cuj) {
return new AnimatorListenerAdapter() {
private boolean mIsCancel;
@@ -112,4 +87,14 @@ public abstract class KeyguardInputView extends LinearLayout {
};
}
+ public void setOnFinishImeAnimationRunnable(Runnable onFinishImeAnimationRunnable) {
+ mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
+ }
+
+ public void runOnFinishImeAnimationRunnable() {
+ if (mOnFinishImeAnimationRunnable != null) {
+ mOnFinishImeAnimationRunnable.run();
+ mOnFinishImeAnimationRunnable = null;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 957882dc9c6b..05f33a9d0997 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -155,8 +155,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final InputMethodManager mInputMethodManager;
private final DelayableExecutor mMainExecutor;
private final Resources mResources;
- private LiftToActivateListener mLiftToActivateListener;
- private TelephonyManager mTelephonyManager;
+ private final LiftToActivateListener mLiftToActivateListener;
+ private final TelephonyManager mTelephonyManager;
private final FalsingCollector mFalsingCollector;
private final boolean mIsNewLayoutEnabled;
@@ -167,8 +167,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
@Main Resources resources, LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager,
- FalsingCollector falsingCollector,
+ TelephonyManager telephonyManager, FalsingCollector falsingCollector,
FeatureFlags featureFlags) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -189,12 +188,13 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
if (keyguardInputView instanceof KeyguardPatternView) {
return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
- keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
+ keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
+ mMessageAreaControllerFactory);
} else if (keyguardInputView instanceof KeyguardPasswordView) {
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mInputMethodManager, mMainExecutor, mResources);
+ mInputMethodManager, mMainExecutor, mResources, mFalsingCollector);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -204,14 +204,14 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mTelephonyManager,
- mFalsingCollector, mIsNewLayoutEnabled);
+ mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
+ mIsNewLayoutEnabled);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mTelephonyManager,
- mFalsingCollector, mIsNewLayoutEnabled);
+ mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
+ mIsNewLayoutEnabled);
}
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 533bec14f60a..cd9627f5bae3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.view.WindowInsets.Type.ime;
+
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
@@ -23,15 +25,25 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
@@ -41,6 +53,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
private final int mDisappearYTranslation;
+ private static final long IME_DISAPPEAR_DURATION_MS = 125;
+
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
@@ -150,9 +164,63 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
}
@Override
- public void animateForIme(float interpolatedFraction) {
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ getWindowInsetsController().controlWindowInsetsAnimation(ime(),
+ 100,
+ Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
+
+ @Override
+ public void onReady(@NonNull WindowInsetsAnimationController controller,
+ int types) {
+ ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+ anim.addUpdateListener(animation -> {
+ if (controller.isCancelled()) {
+ return;
+ }
+ Insets shownInsets = controller.getShownStateInsets();
+ Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
+ (int) (-shownInsets.bottom / 4
+ * anim.getAnimatedFraction())));
+ controller.setInsetsAndAlpha(insets,
+ (float) animation.getAnimatedValue(),
+ anim.getAnimatedFraction());
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ controller.finish(false);
+ runOnFinishImeAnimationRunnable();
+ finishRunnable.run();
+ }
+ });
+ anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ anim.start();
+ }
+
+ @Override
+ public void onFinished(
+ @NonNull WindowInsetsAnimationController controller) {
+ }
+
+ @Override
+ public void onCancelled(
+ @Nullable WindowInsetsAnimationController controller) {
+ }
+ });
+ return true;
+ }
+
+
+ @Override
+ public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
animate().cancel();
- setAlpha(Math.max(interpolatedFraction, getAlpha()));
+ setAlpha(appearingAnim
+ ? Math.max(interpolatedFraction, getAlpha())
+ : 1 - interpolatedFraction);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0f1c3c8a20b7..e45dd8baa1d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -40,6 +40,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -112,9 +113,10 @@ public class KeyguardPasswordViewController
LatencyTracker latencyTracker,
InputMethodManager inputMethodManager,
@Main DelayableExecutor mainExecutor,
- @Main Resources resources) {
+ @Main Resources resources,
+ FalsingCollector falsingCollector) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker);
+ messageAreaControllerFactory, latencyTracker, falsingCollector);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mMainExecutor = mainExecutor;
@@ -187,23 +189,22 @@ public class KeyguardPasswordViewController
return;
}
if (wasDisabled) {
- mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ showInput();
}
}
@Override
public void onResume(int reason) {
super.onResume(reason);
-
- mPasswordEntry.requestFocus();
if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
showInput();
}
}
private void showInput() {
- mPasswordEntry.post(() -> {
- if (mPasswordEntry.isFocused() && mView.isShown()) {
+ mView.post(() -> {
+ if (mView.isShown()) {
+ mPasswordEntry.requestFocus();
mInputMethodManager.showSoftInput(
mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
@@ -212,7 +213,18 @@ public class KeyguardPasswordViewController
@Override
public void onPause() {
- super.onPause();
+ if (!mPasswordEntry.isVisibleToUser()) {
+ // Reset all states directly and then hide IME when the screen turned off.
+ super.onPause();
+ } else {
+ // In order not to break the IME hide animation by resetting states too early after
+ // the password checked, make sure resetting states after the IME hiding animation
+ // finished.
+ mView.setOnFinishImeAnimationRunnable(() -> {
+ mPasswordEntry.clearFocus();
+ super.onPause();
+ });
+ }
mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 2aaf748e2415..4f48bb4f3260 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -23,6 +23,7 @@ import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
import android.os.SystemClock;
+import android.view.MotionEvent;
import android.view.View;
import com.android.internal.util.LatencyTracker;
@@ -35,6 +36,8 @@ import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingClassifier;
+import com.android.systemui.classifier.FalsingCollector;
import java.util.List;
@@ -50,6 +53,7 @@ public class KeyguardPatternViewController
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
private final LatencyTracker mLatencyTracker;
+ private final FalsingCollector mFalsingCollector;
private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
private KeyguardMessageAreaController mMessageAreaController;
@@ -102,6 +106,11 @@ public class KeyguardPatternViewController
final int userId = KeyguardUpdateMonitor.getCurrentUser();
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ // Treat single-sized patterns as erroneous taps.
+ if (pattern.size() == 1) {
+ mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed(
+ 0.7, "empty pattern input"));
+ }
mLockPatternView.enableInput();
onPatternChecked(userId, false, 0, false /* not valid - too short */);
return;
@@ -179,11 +188,13 @@ public class KeyguardPatternViewController
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback,
LatencyTracker latencyTracker,
+ FalsingCollector falsingCollector,
KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
super(view, securityMode, keyguardSecurityCallback);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
+ mFalsingCollector = falsingCollector;
mMessageAreaControllerFactory = messageAreaControllerFactory;
KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
mMessageAreaController = mMessageAreaControllerFactory.create(kma);
@@ -205,6 +216,12 @@ public class KeyguardPatternViewController
KeyguardUpdateMonitor.getCurrentUser()));
// vibrate mode will be the same for the life of this screen
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+ mLockPatternView.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFalsingCollector.avoidGesture();
+ }
+ return false;
+ });
EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
if (button != null) {
@@ -224,6 +241,7 @@ public class KeyguardPatternViewController
protected void onViewDetached() {
super.onViewDetached();
mLockPatternView.setOnPatternListener(null);
+ mLockPatternView.setOnTouchListener(null);
EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
if (button != null) {
button.setCallback(null);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 4e06491621cb..09fb8efba4e8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -164,6 +164,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
reloadColors();
}
+ NumPadKey[] getButtons() {
+ return mButtons;
+ }
+
/**
* By default, the new layout will be enabled. When false, revert to the old style.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index f2479488db0f..b156f8169b50 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,7 +25,6 @@ import android.view.View.OnTouchListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
@@ -50,19 +49,6 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
return false;
};
- private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- mFalsingCollector.avoidGesture();
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return false;
- }
- };
-
protected KeyguardPinBasedInputViewController(T view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode,
@@ -73,7 +59,7 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
LiftToActivateListener liftToActivateListener,
FalsingCollector falsingCollector) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker);
+ messageAreaControllerFactory, latencyTracker, falsingCollector);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
@@ -83,8 +69,14 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
protected void onViewAttached() {
super.onViewAttached();
- mView.addMotionEventListener(mGlobalTouchListener);
-
+ for (NumPadKey button: mView.getButtons()) {
+ button.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFalsingCollector.avoidGesture();
+ }
+ return false;
+ });
+ }
mPasswordEntry.setOnKeyListener(mOnKeyListener);
mPasswordEntry.setUserActivityListener(this::onUserInput);
@@ -123,7 +115,10 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
@Override
protected void onViewDetached() {
super.onViewDetached();
- mView.removeMotionEventListener(mGlobalTouchListener);
+
+ for (NumPadKey button: mView.getButtons()) {
+ button.setOnTouchListener(null);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a2d7707a1569..4887767b9922 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -23,11 +23,9 @@ import static java.lang.Integer.max;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
-import android.graphics.Insets;
import android.graphics.Rect;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -42,12 +40,9 @@ import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
-import android.view.WindowInsetsAnimationControlListener;
-import android.view.WindowInsetsAnimationController;
import android.view.WindowManager;
import android.widget.FrameLayout;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -58,10 +53,12 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import java.util.ArrayList;
import java.util.List;
public class KeyguardSecurityContainer extends FrameLayout {
@@ -97,6 +94,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
private final ViewConfiguration mViewConfiguration;
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
+ private final List<Gefingerpoken> mMotionEventListeners = new ArrayList<>();
private float mLastTouchY = -1;
private int mActivePointerId = -1;
@@ -127,6 +125,9 @@ public class KeyguardSecurityContainer extends FrameLayout {
WindowInsetsAnimation.Bounds bounds) {
if (!mDisappearAnimRunning) {
beginJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
+ } else {
+ beginJankInstrument(
+ InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
}
mSecurityViewFlipper.getBoundsOnScreen(mFinalBounds);
return bounds;
@@ -135,25 +136,28 @@ public class KeyguardSecurityContainer extends FrameLayout {
@Override
public WindowInsets onProgress(WindowInsets windowInsets,
List<WindowInsetsAnimation> list) {
- if (mDisappearAnimRunning) {
- mSecurityViewFlipper.setTranslationY(
- mInitialBounds.bottom - mFinalBounds.bottom);
- } else {
- int translationY = 0;
- float interpolatedFraction = 1f;
- for (WindowInsetsAnimation animation : list) {
- if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
- continue;
- }
- interpolatedFraction = animation.getInterpolatedFraction();
-
- final int paddingBottom = (int) MathUtils.lerp(
- mInitialBounds.bottom - mFinalBounds.bottom, 0,
- interpolatedFraction);
- translationY += paddingBottom;
+ float start = mDisappearAnimRunning
+ ? -(mFinalBounds.bottom - mInitialBounds.bottom)
+ : mInitialBounds.bottom - mFinalBounds.bottom;
+ float end = mDisappearAnimRunning
+ ? -((mFinalBounds.bottom - mInitialBounds.bottom) * 0.75f)
+ : 0f;
+ int translationY = 0;
+ float interpolatedFraction = 1f;
+ for (WindowInsetsAnimation animation : list) {
+ if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
+ continue;
}
- mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction);
+ interpolatedFraction = animation.getInterpolatedFraction();
+
+ final int paddingBottom = (int) MathUtils.lerp(
+ start, end,
+ interpolatedFraction);
+ translationY += paddingBottom;
}
+ mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
+ !mDisappearAnimRunning);
+
return windowInsets;
}
@@ -161,7 +165,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
public void onEnd(WindowInsetsAnimation animation) {
if (!mDisappearAnimRunning) {
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
- mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f);
+ mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
+ true /* appearingAnim */);
+ } else {
+ endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
}
}
};
@@ -388,6 +395,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ boolean result = mMotionEventListeners.stream().anyMatch(
+ listener -> listener.onInterceptTouchEvent(event))
+ || super.onInterceptTouchEvent(event);
+
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
int pointerIndex = event.getActionIndex();
@@ -418,12 +429,17 @@ public class KeyguardSecurityContainer extends FrameLayout {
mIsDragging = false;
break;
}
- return false;
+ return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
+
+ boolean result = mMotionEventListeners.stream()
+ .anyMatch(listener -> listener.onTouchEvent(event))
+ || super.onTouchEvent(event);
+
switch (action) {
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
@@ -469,6 +485,14 @@ public class KeyguardSecurityContainer extends FrameLayout {
return true;
}
+ void addMotionEventListener(Gefingerpoken listener) {
+ mMotionEventListeners.add(listener);
+ }
+
+ void removeMotionEventListener(Gefingerpoken listener) {
+ mMotionEventListeners.remove(listener);
+ }
+
private void handleTap(MotionEvent event) {
// If we're using a fullscreen security mode, skip
if (!mOneHandedMode) {
@@ -502,63 +526,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
- if (securitySelection == SecurityMode.Password) {
- mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
- IME_DISAPPEAR_DURATION_MS,
- Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
-
-
- @Override
- public void onReady(@NonNull WindowInsetsAnimationController controller,
- int types) {
- ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
- anim.addUpdateListener(animation -> {
- if (controller.isCancelled()) {
- return;
- }
- Insets shownInsets = controller.getShownStateInsets();
- Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
- (int) (-shownInsets.bottom / 4
- * anim.getAnimatedFraction())));
- controller.setInsetsAndAlpha(insets,
- (float) animation.getAnimatedValue(),
- anim.getAnimatedFraction());
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- beginJankInstrument(
- InteractionJankMonitor
- .CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- endJankInstrument(
- InteractionJankMonitor
- .CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
- controller.finish(false);
- }
- });
- anim.setDuration(IME_DISAPPEAR_DURATION_MS);
- anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- anim.start();
- }
-
- @Override
- public void onFinished(
- @NonNull WindowInsetsAnimationController controller) {
- mDisappearAnimRunning = false;
- }
-
- @Override
- public void onCancelled(
- @Nullable WindowInsetsAnimationController controller) {
- cancelJankInstrument(
- InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
- }
- });
- }
}
private void beginJankInstrument(int cuj) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db67431..ccba1d59c8d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -33,6 +33,7 @@ import android.metrics.LogMaker;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import android.view.MotionEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -46,6 +47,7 @@ import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -74,6 +76,34 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
+ private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+ private MotionEvent mTouchDown;
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Do just a bit of our own falsing. People should only be tapping on the input, not
+ // swiping.
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ if (mTouchDown != null) {
+ mTouchDown.recycle();
+ mTouchDown = null;
+ }
+ mTouchDown = MotionEvent.obtain(ev);
+ } else if (mTouchDown != null) {
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP
+ || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ mTouchDown.recycle();
+ mTouchDown = null;
+ }
+ }
+ return false;
+ }
+ };
+
private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
public void userActivity() {
if (mSecurityCallback != null) {
@@ -192,12 +222,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
@Override
protected void onViewAttached() {
mView.setSwipeListener(mSwipeListener);
+ mView.addMotionEventListener(mGlobalTouchListener);
mConfigurationController.addCallback(mConfigurationListener);
}
@Override
protected void onViewDetached() {
mConfigurationController.removeCallback(mConfigurationListener);
+ mView.removeMotionEventListener(mGlobalTouchListener);
}
/** */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 75ef4b32dfda..e01e17dc6006 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -87,10 +87,10 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper {
* Translate the entire view, and optionally inform the wrapped view of the progress
* so it can animate with the parent.
*/
- public void animateForIme(int translationY, float interpolatedFraction) {
+ public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
super.setTranslationY(translationY);
KeyguardInputView v = getSecurityView();
- if (v != null) v.animateForIme(interpolatedFraction);
+ if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index cdbbfe643812..b2bf2e674b33 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -78,8 +78,8 @@ public class KeyguardSimPinViewController
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager,
- FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+ TelephonyManager telephonyManager, FalsingCollector falsingCollector,
+ boolean isNewLayoutEnabled) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
falsingCollector);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 8fff34278216..620db481e4a2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -85,8 +85,8 @@ public class KeyguardSimPukViewController
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager,
- FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+ TelephonyManager telephonyManager, FalsingCollector falsingCollector,
+ boolean isNewLayoutEnabled) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
falsingCollector);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a4054bea1167..69e6ed043172 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2077,8 +2077,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean shouldListenForUdfps() {
return shouldListenForFingerprint()
&& !mBouncer
- && mStatusBarState != StatusBarState.SHADE_LOCKED
- && mStatusBarState != StatusBarState.FULLSCREEN_USER_SWITCHER
&& mStrongAuthTracker.hasUserAuthenticatedSinceBoot();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 97d6e9750716..6cee4a4d4ad7 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -15,6 +15,7 @@
*/
package com.android.keyguard;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -23,7 +24,6 @@ import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.view.ContextThemeWrapper;
-import android.view.ViewGroup;
import androidx.annotation.StyleRes;
@@ -34,11 +34,12 @@ import com.android.systemui.R;
* Provides background color and radius animations for key pad buttons.
*/
class NumPadAnimator {
- private ValueAnimator mAnimator;
+ private AnimatorSet mAnimator;
+ private ValueAnimator mExpandAnimator;
+ private ValueAnimator mContractAnimator;
private GradientDrawable mBackground;
private RippleDrawable mRipple;
private GradientDrawable mRippleMask;
- private int mMargin;
private int mNormalColor;
private int mHighlightColor;
private int mStyle;
@@ -52,32 +53,37 @@ class NumPadAnimator {
reloadColors(context);
- mMargin = context.getResources().getDimensionPixelSize(R.dimen.num_pad_key_margin);
-
// Actual values will be updated later, usually during an onLayout() call
- mAnimator = ValueAnimator.ofFloat(0f);
- mAnimator.setDuration(100);
- mAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- mAnimator.setRepeatMode(ValueAnimator.REVERSE);
- mAnimator.setRepeatCount(1);
- mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ mAnimator = new AnimatorSet();
+ mExpandAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mExpandAnimator.setDuration(50);
+ mExpandAnimator.setInterpolator(Interpolators.LINEAR);
+ mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator anim) {
mBackground.setCornerRadius((float) anim.getAnimatedValue());
mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
}
});
- }
-
- void updateMargin(ViewGroup.MarginLayoutParams lp) {
- lp.setMargins(mMargin, mMargin, mMargin, mMargin);
+ mContractAnimator = ValueAnimator.ofFloat(1f, 0f);
+ mContractAnimator.setStartDelay(33);
+ mContractAnimator.setDuration(417);
+ mContractAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mContractAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator anim) {
+ mBackground.setCornerRadius((float) anim.getAnimatedValue());
+ mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
+ }
+ });
+ mAnimator.playSequentially(mExpandAnimator, mContractAnimator);
}
void onLayout(int height) {
float startRadius = height / 2f;
float endRadius = height / 4f;
mBackground.setCornerRadius(startRadius);
- mAnimator.setFloatValues(startRadius, endRadius);
+ mExpandAnimator.setFloatValues(startRadius, endRadius);
+ mContractAnimator.setFloatValues(endRadius, startRadius);
}
void start() {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 5db3349b646a..b76499a39ff8 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -17,12 +17,14 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.VectorDrawable;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.MotionEvent;
-import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -32,20 +34,19 @@ import com.android.systemui.R;
*/
public class NumPadButton extends AlphaOptimizedImageButton {
+ @Nullable
private NumPadAnimator mAnimator;
public NumPadButton(Context context, AttributeSet attrs) {
super(context, attrs);
- mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
- attrs.getStyleAttribute());
- }
-
- @Override
- public void setLayoutParams(ViewGroup.LayoutParams params) {
- if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params);
-
- super.setLayoutParams(params);
+ Drawable background = getBackground();
+ if (background instanceof LayerDrawable) {
+ mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
+ attrs.getStyleAttribute());
+ } else {
+ mAnimator = null;
+ }
}
@Override
@@ -93,9 +94,11 @@ public class NumPadButton extends AlphaOptimizedImageButton {
* By default, the new layout will be enabled. Invoking will revert to the old style
*/
public void disableNewLayout() {
- mAnimator = null;
- ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
- setBackground(getContext().getResources().getDrawable(
- R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ if (mAnimator != null) {
+ mAnimator = null;
+ ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
+ setBackground(getContext().getResources().getDrawable(
+ R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index e6a9d4fdd547..89c1a7fe21ca 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -31,6 +32,8 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -48,6 +51,7 @@ public class NumPadKey extends ViewGroup {
private int mTextViewResId;
private PasswordTextView mTextView;
+ @Nullable
private NumPadAnimator mAnimator;
private View.OnClickListener mListener = new View.OnClickListener() {
@@ -127,8 +131,13 @@ public class NumPadKey extends ViewGroup {
setContentDescription(mDigitText.getText().toString());
- mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
- R.style.NumPadKey);
+ Drawable background = getBackground();
+ if (background instanceof LayerDrawable) {
+ mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
+ R.style.NumPadKey);
+ } else {
+ mAnimator = null;
+ }
}
/**
@@ -136,10 +145,12 @@ public class NumPadKey extends ViewGroup {
*/
public void disableNewLayout() {
findViewById(R.id.klondike_text).setVisibility(View.VISIBLE);
- mAnimator = null;
- ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
- setBackground(getContext().getResources().getDrawable(
- R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ if (mAnimator != null) {
+ mAnimator = null;
+ ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
+ setBackground(getContext().getResources().getDrawable(
+ R.drawable.ripple_drawable_pin, ctw.getTheme()));
+ }
}
/**
@@ -167,13 +178,6 @@ public class NumPadKey extends ViewGroup {
}
@Override
- public void setLayoutParams(ViewGroup.LayoutParams params) {
- if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params);
-
- super.setLayoutParams(params);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 5ffc2836b9e3..b80f8bd64dcf 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -121,9 +121,19 @@ public class PasswordTextView extends View {
public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setFocusableInTouchMode(true);
- setFocusable(true);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
+ TypedArray a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
+ try {
+ // If defined, use the provided values. If not, set them to true by default.
+ boolean isFocusable = a.getBoolean(android.R.styleable.View_focusable,
+ /* defValue= */ true);
+ boolean isFocusableInTouchMode = a.getBoolean(
+ android.R.styleable.View_focusableInTouchMode, /* defValue= */ true);
+ setFocusable(isFocusable);
+ setFocusableInTouchMode(isFocusableInTouchMode);
+ } finally {
+ a.recycle();
+ }
+ a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
try {
mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0);
mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a9b4c492e10c..7966b388bc4f 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -25,9 +25,12 @@ import android.os.HandlerThread;
import android.os.SystemClock;
import android.os.Trace;
import android.service.wallpaper.WallpaperService;
+import android.util.ArraySet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Size;
import android.view.SurfaceHolder;
+import android.view.WindowManager;
import androidx.annotation.NonNull;
@@ -54,7 +57,10 @@ public class ImageWallpaper extends WallpaperService {
private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS =
new RectF(0, 0, 1, 1);
private static final boolean DEBUG = false;
- private ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
+ private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
+ private final ArraySet<RectF> mColorAreas = new ArraySet<>();
+ private float mShift;
+ private volatile int mPages;
private HandlerThread mWorker;
// scaled down version
private Bitmap mMiniBitmap;
@@ -96,6 +102,10 @@ public class ImageWallpaper extends WallpaperService {
private EglHelper mEglHelper;
private final Runnable mFinishRenderingTask = this::finishRendering;
private boolean mNeedRedraw;
+ private int mWidth = 1;
+ private int mHeight = 1;
+ private int mImgWidth = 1;
+ private int mImgHeight = 1;
GLEngine() {
}
@@ -111,8 +121,13 @@ public class ImageWallpaper extends WallpaperService {
// Deferred init renderer because we need to get wallpaper by display context.
mRenderer = getRendererInstance();
setFixedSizeAllowed(true);
- setOffsetNotificationsEnabled(false);
updateSurfaceSize();
+ Rect window = getDisplayContext()
+ .getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics()
+ .getBounds();
+ mHeight = window.height();
+ mWidth = window.width();
mMiniBitmap = null;
if (mWorker != null && mWorker.getThreadHandler() != null) {
mWorker.getThreadHandler().post(this::updateMiniBitmap);
@@ -127,6 +142,41 @@ public class ImageWallpaper extends WallpaperService {
return new ImageWallpaperRenderer(getDisplayContext());
}
+ @Override
+ public void onOffsetsChanged(float xOffset, float yOffset,
+ float xOffsetStep, float yOffsetStep,
+ int xPixelOffset, int yPixelOffset) {
+ if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
+ final int pages;
+ if (xOffsetStep > 0 && xOffsetStep <= 1) {
+ pages = (int) (1 / xOffsetStep + 1);
+ } else {
+ pages = 1;
+ }
+ if (pages == mPages) return;
+ mPages = pages;
+ updateShift();
+ mWorker.getThreadHandler().post(() ->
+ computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
+ }
+
+ private void updateShift() {
+ if (mImgHeight == 0) {
+ mShift = 0;
+ return;
+ }
+ // calculate shift
+ float imgWidth = (float) mImgWidth / (float) mImgHeight;
+ float displayWidth =
+ (float) mWidth / (float) mHeight;
+ // if need to shift
+ if (imgWidth > displayWidth) {
+ mShift = imgWidth / imgWidth - displayWidth / imgWidth;
+ } else {
+ mShift = 0;
+ }
+ }
+
private void updateMiniBitmap() {
mRenderer.useBitmap(b -> {
int size = Math.min(b.getWidth(), b.getHeight());
@@ -134,6 +184,8 @@ public class ImageWallpaper extends WallpaperService {
if (size > MIN_SURFACE_WIDTH) {
scale = (float) MIN_SURFACE_WIDTH / (float) size;
}
+ mImgHeight = b.getHeight();
+ mImgWidth = b.getWidth();
mMiniBitmap = Bitmap.createScaledBitmap(b, Math.round(scale * b.getWidth()),
Math.round(scale * b.getHeight()), false);
computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
@@ -173,6 +225,9 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void addLocalColorsAreas(@NonNull List<RectF> regions) {
mWorker.getThreadHandler().post(() -> {
+ if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
+ setOffsetNotificationsEnabled(true);
+ }
Bitmap bitmap = mMiniBitmap;
if (bitmap == null) {
mLocalColorsToAdd.addAll(regions);
@@ -184,6 +239,7 @@ public class ImageWallpaper extends WallpaperService {
private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) {
List<WallpaperColors> colors = getLocalWallpaperColors(regions, b);
+ mColorAreas.addAll(regions);
try {
notifyLocalColorsChanged(regions, colors);
} catch (RuntimeException e) {
@@ -193,14 +249,45 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
- // No-OP
+ mWorker.getThreadHandler().post(() -> {
+ mColorAreas.removeAll(regions);
+ mLocalColorsToAdd.removeAll(regions);
+ if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
+ setOffsetNotificationsEnabled(false);
+ }
+ });
+ }
+
+ private RectF pageToImgRect(RectF area) {
+ float pageWidth = 1f / (float) mPages;
+ if (pageWidth < 1 && pageWidth >= 0) pageWidth = 1;
+ float imgWidth = (float) mImgWidth / (float) mImgHeight;
+ float displayWidth =
+ (float) mWidth / (float) mHeight;
+ float expansion = imgWidth > displayWidth ? displayWidth / imgWidth : 1;
+ int page = (int) Math.floor(area.centerX() / pageWidth);
+ float shiftWidth = mShift * page * pageWidth;
+ RectF imgArea = new RectF();
+ imgArea.bottom = area.bottom;
+ imgArea.top = area.top;
+ imgArea.left = MathUtils.constrain(area.left % pageWidth, 0, 1)
+ * expansion + shiftWidth;
+ imgArea.right = MathUtils.constrain(area.right % pageWidth, 0, 1)
+ * expansion + shiftWidth;
+ if (imgArea.left > imgArea.right) {
+ // take full page
+ imgArea.left = shiftWidth;
+ imgArea.right = 1 - (mShift - shiftWidth);
+ }
+ return imgArea;
}
private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
Bitmap b) {
List<WallpaperColors> colors = new ArrayList<>(areas.size());
+ updateShift();
for (int i = 0; i < areas.size(); i++) {
- RectF area = areas.get(i);
+ RectF area = pageToImgRect(areas.get(i));
if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
colors.add(null);
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 3852b24fe4b3..fba34e0b8ad3 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -367,7 +367,7 @@ public class SwipeHelper implements Gefingerpoken {
}
/**
- * @param view The view to be dismissed
+ * @param animView The view to be dismissed
* @param velocity The desired pixels/second speed at which the view should move
* @param endAction The action to perform at the end
* @param delay The delay after which we should start
@@ -477,12 +477,8 @@ public class SwipeHelper implements Gefingerpoken {
public void snapChild(final View animView, final float targetLeft, float velocity) {
final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
- AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
- }
- };
+ AnimatorUpdateListener updateListener = animation -> onTranslationUpdate(animView,
+ (float) animation.getAnimatedValue(), canBeDismissed);
Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
if (anim == null) {
@@ -501,8 +497,6 @@ public class SwipeHelper implements Gefingerpoken {
mSnappingChild = false;
if (!wasCancelled) {
updateSwipeProgressFromOffset(animView, canBeDismissed);
- onChildSnappedBack(animView, targetLeft);
- mCallback.onChildSnappedBack(animView, targetLeft);
resetSwipeState();
}
}
@@ -513,6 +507,7 @@ public class SwipeHelper implements Gefingerpoken {
mFlingAnimationUtils.apply(anim, getTranslation(animView), targetLeft, velocity,
maxDistance);
anim.start();
+ mCallback.onChildSnappedBack(animView, targetLeft);
}
/**
@@ -594,13 +589,12 @@ public class SwipeHelper implements Gefingerpoken {
if (!mIsSwiping && !mMenuRowIntercepting) {
if (mCallback.getChildAtPosition(ev) != null) {
-
// We are dragging directly over a card, make sure that we also catch the gesture
// even if nobody else wants the touch event.
+ mTouchedView = mCallback.getChildAtPosition(ev);
onInterceptTouchEvent(ev);
return true;
} else {
-
// We are not doing anything, make sure the long press callback
// is not still ticking like a bomb waiting to go off.
cancelLongPress();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index c1cf8d31bd67..9f77b7d26c96 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -19,6 +19,7 @@ package com.android.systemui.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import android.annotation.NonNull;
+import android.annotation.UiContext;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
@@ -69,7 +70,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private final MagnificationGestureDetector mGestureDetector;
private boolean mSingleTapDetected = false;
- MagnificationModeSwitch(Context context) {
+ MagnificationModeSwitch(@UiContext Context context) {
this(context, createView(context));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index 50f6e9f6af44..1a01ad85fccc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -106,10 +108,9 @@ public class ModeSwitchesController {
@Override
protected MagnificationModeSwitch createInstance(Display display) {
- final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
- ? mContext
- : mContext.createDisplayContext(display);
- return new MagnificationModeSwitch(context);
+ final Context uiContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
+ return new MagnificationModeSwitch(uiContext);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 1bf747408d95..c051b695e823 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -53,12 +53,18 @@ import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
+import com.android.systemui.util.Assert;
import java.util.Locale;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Class to register system actions with accessibility framework.
*/
@@ -128,23 +134,39 @@ public class SystemActions extends SystemUI {
public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT =
AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT; // 13
+ public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
+ AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15
+
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
- private SystemActionsBroadcastReceiver mReceiver;
+ private final SystemActionsBroadcastReceiver mReceiver;
private Locale mLocale;
- private AccessibilityManager mA11yManager;
+ private final AccessibilityManager mA11yManager;
+ private final Lazy<StatusBar> mStatusBar;
+ private final NotificationShadeWindowController mNotificationShadeController;
+ private final StatusBarWindowCallback mNotificationShadeCallback;
+ private boolean mDismissNotificationShadeActionRegistered;
@Inject
- public SystemActions(Context context) {
+ public SystemActions(Context context,
+ NotificationShadeWindowController notificationShadeController,
+ Lazy<StatusBar> statusBar) {
super(context);
mReceiver = new SystemActionsBroadcastReceiver();
mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
mA11yManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
+ mNotificationShadeController = notificationShadeController;
+ // Saving in instance variable since to prevent GC since
+ // NotificationShadeWindowController.registerCallback() only keeps weak references.
+ mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) ->
+ registerOrUnregisterDismissNotificationShadeAction();
+ mStatusBar = statusBar;
}
@Override
public void start() {
+ mNotificationShadeController.registerCallback(mNotificationShadeCallback);
mContext.registerReceiverForAllUsers(
mReceiver,
mReceiver.createIntentFilter(),
@@ -210,6 +232,32 @@ public class SystemActions extends SystemUI {
mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
mA11yManager.registerSystemAction(
actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT);
+ registerOrUnregisterDismissNotificationShadeAction();
+ }
+
+ private void registerOrUnregisterDismissNotificationShadeAction() {
+ Assert.isMainThread();
+
+ // Saving state in instance variable since this callback is called quite often to avoid
+ // binder calls
+ StatusBar statusBar = mStatusBar.get();
+ if (statusBar.isPanelExpanded() && !statusBar.isKeyguardShowing()) {
+ if (!mDismissNotificationShadeActionRegistered) {
+ mA11yManager.registerSystemAction(
+ createRemoteAction(
+ R.string.accessibility_system_action_dismiss_notification_shade,
+ SystemActionsBroadcastReceiver
+ .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE),
+ SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+ mDismissNotificationShadeActionRegistered = true;
+ }
+ } else {
+ if (mDismissNotificationShadeActionRegistered) {
+ mA11yManager.unregisterSystemAction(
+ SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+ mDismissNotificationShadeActionRegistered = false;
+ }
+ }
}
/**
@@ -261,10 +309,15 @@ public class SystemActions extends SystemUI {
R.string.accessibility_system_action_on_screen_a11y_shortcut_chooser_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER;
break;
- case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
+ case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
labelId = R.string.accessibility_system_action_hardware_a11y_shortcut_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT;
break;
+ case SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
+ labelId = R.string.accessibility_system_action_dismiss_notification_shade;
+ intent = SystemActionsBroadcastReceiver
+ .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE;
+ break;
default:
return;
}
@@ -369,6 +422,10 @@ public class SystemActions extends SystemUI {
mA11yManager.performAccessibilityShortcut();
}
+ private void handleAccessibilityDismissNotificationShade() {
+ mStatusBar.get().animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, false /* force */);
+ }
+
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -384,6 +441,8 @@ public class SystemActions extends SystemUI {
"SYSTEM_ACTION_ACCESSIBILITY_BUTTON_MENU";
private static final String INTENT_ACTION_ACCESSIBILITY_SHORTCUT =
"SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT";
+ private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
+ "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE";
private PendingIntent createPendingIntent(Context context, String intentAction) {
switch (intentAction) {
@@ -397,7 +456,8 @@ public class SystemActions extends SystemUI {
case INTENT_ACTION_TAKE_SCREENSHOT:
case INTENT_ACTION_ACCESSIBILITY_BUTTON:
case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER:
- case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: {
+ case INTENT_ACTION_ACCESSIBILITY_SHORTCUT:
+ case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
Intent intent = new Intent(intentAction);
intent.setPackage(context.getPackageName());
return PendingIntent.getBroadcast(context, 0, intent,
@@ -422,6 +482,7 @@ public class SystemActions extends SystemUI {
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
+ intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
return intentFilter;
}
@@ -473,6 +534,10 @@ public class SystemActions extends SystemUI {
handleAccessibilityShortcut();
break;
}
+ case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
+ handleAccessibilityDismissNotificationShade();
+ break;
+ }
default:
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index f52dcd596a55..cdd69429132a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.content.Context;
@@ -82,9 +84,8 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
@Override
protected WindowMagnificationAnimationController createInstance(Display display) {
- final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
- ? mContext
- : mContext.createDisplayContext(display);
+ final Context windowContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
final WindowMagnificationController controller = new WindowMagnificationController(
mContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
@@ -92,7 +93,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
final int navBarMode = mNavigationModeController.addListener(
controller::onNavigationModeChanged);
controller.onNavigationModeChanged(navBarMode);
- return new WindowMagnificationAnimationController(context, controller);
+ return new WindowMagnificationAnimationController(windowContext, controller);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 24d83884f093..5758b1575f5a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -20,6 +20,7 @@ import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.UiContext;
import android.content.Context;
import android.content.res.Resources;
import android.os.RemoteException;
@@ -69,8 +70,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
@MagnificationState
private int mState = STATE_DISABLED;
- WindowMagnificationAnimationController(
- Context context, WindowMagnificationController controller) {
+ WindowMagnificationAnimationController(@UiContext Context context,
+ WindowMagnificationController controller) {
this(context, controller, newValueAnimator(context.getResources()));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 3f7d2d837e2d..2b666f13efcc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -24,6 +24,7 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UiContext;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
@@ -134,7 +135,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@Nullable
private MirrorWindowControl mMirrorWindowControl;
- WindowMagnificationController(Context context, @NonNull Handler handler,
+ WindowMagnificationController(@UiContext Context context, @NonNull Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
@NonNull WindowMagnifierCallback callback) {
@@ -147,7 +148,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mDisplayId = mContext.getDisplayId();
mRotation = display.getRotation();
- mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mWm = context.getSystemService(WindowManager.class);
mResources = mContext.getResources();
mScale = mResources.getInteger(R.integer.magnification_default_scale);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 2040347de1b5..e53f5c97bb5c 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -20,7 +20,6 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
-import android.Manifest;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -43,6 +42,7 @@ import androidx.annotation.WorkerThread;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -370,13 +370,9 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
}
// TODO ntmyren: remove after teamfood is finished
- private boolean shouldShowAppPredictor(String pkgName) {
- if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
- false)) {
- return false;
- }
- return mPackageManager.checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, pkgName)
- == PackageManager.PERMISSION_GRANTED;
+ private boolean showSystemApps() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
}
/**
@@ -399,8 +395,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
return true;
}
// TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
- if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
- || shouldShowAppPredictor(packageName)
+ if (((showSystemApps() && !packageName.equals("android"))
+ || appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
|| isSpeechRecognizerUsage(appOpCode, packageName)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
index cc608ef87bc6..007080bc8603 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
@@ -16,17 +16,22 @@
package com.android.systemui.biometrics;
+import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Surface;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
/**
@@ -51,53 +56,38 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
mSensorProps = prop;
}
- /**
- * For devices where the sensor is too high up, calculates the amount of padding necessary to
- * move/center the biometric icon within the sensor's physical location.
- */
- static int calculateBottomSpacerHeight(int displayHeightPx, int navbarHeightPx,
- int dialogBottomMarginPx, @NonNull View buttonBar, @NonNull View textIndicator,
- @NonNull FingerprintSensorPropertiesInternal sensorProperties) {
- final int sensorDistanceFromBottom = displayHeightPx - sensorProperties.sensorLocationY
- - sensorProperties.sensorRadius;
-
- final int spacerHeight = sensorDistanceFromBottom
- - textIndicator.getMeasuredHeight()
- - buttonBar.getMeasuredHeight()
- - dialogBottomMarginPx
- - navbarHeightPx;
-
- Log.d(TAG, "Display height: " + displayHeightPx
- + ", Distance from bottom: " + sensorDistanceFromBottom
- + ", Bottom margin: " + dialogBottomMarginPx
- + ", Navbar height: " + navbarHeightPx
- + ", Spacer height: " + spacerHeight);
-
- return spacerHeight;
- }
-
@Override
+ @NonNull
AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
- final View spaceBelowIcon = findViewById(R.id.space_below_icon);
- spaceBelowIcon.setVisibility(View.VISIBLE);
+ final int displayRotation = getDisplay().getRotation();
+ switch (displayRotation) {
+ case Surface.ROTATION_0:
+ return onMeasureInternalPortrait(width, height);
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ return onMeasureInternalLandscape(width, height);
+ default:
+ Log.e(TAG, "Unsupported display rotation: " + displayRotation);
+ return super.onMeasureInternal(width, height);
+ }
+ }
+ @NonNull
+ private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height) {
// Get the height of the everything below the icon. Currently, that's the indicator and
- // button bar
- final View textIndicator = findViewById(R.id.indicator);
- final View buttonBar = findViewById(R.id.button_bar);
+ // button bar.
+ final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
+ final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
// Figure out where the bottom of the sensor anim should be.
// Navbar + dialogMargin + buttonBar + textIndicator + spacerHeight = sensorDistFromBottom
- final int dialogBottomMarginPx = getResources()
- .getDimensionPixelSize(R.dimen.biometric_dialog_border_padding);
- final WindowManager wm = getContext().getSystemService(WindowManager.class);
- final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
- final int navbarHeight = wm.getCurrentWindowMetrics().getWindowInsets()
- .getInsets(WindowInsets.Type.navigationBars()).toRect().height();
- final int displayHeight = bounds.height();
-
- final int spacerHeight = calculateBottomSpacerHeight(displayHeight, navbarHeight,
- dialogBottomMarginPx, buttonBar, textIndicator, mSensorProps);
+ final int dialogMargin = getDialogMarginPx();
+ final WindowManager windowManager = getContext().getSystemService(WindowManager.class);
+ final int displayHeight = getWindowBounds(windowManager).height();
+ final Insets navbarInsets = getNavbarInsets(windowManager);
+ final int bottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
+ mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
+ dialogMargin, navbarInsets.bottom);
// Go through each of the children and do the custom measurement.
int totalHeight = 0;
@@ -105,22 +95,25 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
final int sensorDiameter = mSensorProps.sensorRadius * 2;
for (int i = 0; i < numChildren; i++) {
final View child = getChildAt(i);
-
if (child.getId() == R.id.biometric_icon_frame) {
- // Create a frame that's exactly the size of the sensor circle
- child.measure(
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.biometric_icon) {
- // Icon should never be larger than the circle
- child.measure(
+ final FrameLayout iconFrame = (FrameLayout) child;
+ final View icon = iconFrame.getChildAt(0);
+
+ // Ensure that the icon is never larger than the sensor.
+ icon.measure(
MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));
+
+ // Create a frame that's exactly the height of the sensor circle.
+ iconFrame.measure(
+ MeasureSpec.makeMeasureSpec(
+ child.getLayoutParams().width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
} else if (child.getId() == R.id.space_above_icon) {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
- MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(
+ child.getLayoutParams().height, MeasureSpec.EXACTLY));
} else if (child.getId() == R.id.button_bar) {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -128,8 +121,9 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
MeasureSpec.EXACTLY));
} else if (child.getId() == R.id.space_below_icon) {
// Set the spacer height so the fingerprint icon is on the physical sensor area
- child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(spacerHeight, MeasureSpec.EXACTLY));
+ child.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
} else {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -143,4 +137,184 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
return new AuthDialog.LayoutParams(width, totalHeight);
}
+
+ @NonNull
+ private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height) {
+ // Find the spacer height needed to vertically align the icon with the sensor.
+ final int titleHeight = getViewHeightPx(R.id.title);
+ final int subtitleHeight = getViewHeightPx(R.id.subtitle);
+ final int descriptionHeight = getViewHeightPx(R.id.description);
+ final int topSpacerHeight = getViewHeightPx(R.id.space_above_icon);
+ final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
+ final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
+ final WindowManager windowManager = getContext().getSystemService(WindowManager.class);
+ final Insets navbarInsets = getNavbarInsets(windowManager);
+ final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight,
+ subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight,
+ buttonBarHeight, navbarInsets.bottom);
+
+ // Find the spacer width needed to horizontally align the icon with the sensor.
+ final int displayWidth = getWindowBounds(windowManager).width();
+ final int dialogMargin = getDialogMarginPx();
+ final int horizontalInset = navbarInsets.left + navbarInsets.right;
+ final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
+ mSensorProps, displayWidth, dialogMargin, horizontalInset);
+
+ final int sensorDiameter = mSensorProps.sensorRadius * 2;
+ final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;
+
+ int remeasuredHeight = 0;
+ final int numChildren = getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ final View child = getChildAt(i);
+ if (child.getId() == R.id.biometric_icon_frame) {
+ final FrameLayout iconFrame = (FrameLayout) child;
+ final View icon = iconFrame.getChildAt(0);
+
+ // Ensure that the icon is never larger than the sensor.
+ icon.measure(
+ MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));
+
+ // Create a frame that's exactly the height of the sensor circle.
+ iconFrame.measure(
+ MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
+ } else if (child.getId() == R.id.space_above_icon || child.getId() == R.id.button_bar) {
+ // Adjust the width of the top spacer and button bar while preserving their heights.
+ child.measure(
+ MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(
+ child.getLayoutParams().height, MeasureSpec.EXACTLY));
+ } else if (child.getId() == R.id.space_below_icon) {
+ // Adjust the bottom spacer height to align the fingerprint icon with the sensor.
+ child.measure(
+ MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
+ } else {
+ // Use the remeasured width for all other child views.
+ child.measure(
+ MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+ }
+
+ if (child.getVisibility() != View.GONE) {
+ remeasuredHeight += child.getMeasuredHeight();
+ }
+ }
+
+ return new AuthDialog.LayoutParams(remeasuredWidth, remeasuredHeight);
+ }
+
+ private int getViewHeightPx(@IdRes int viewId) {
+ final View view = findViewById(viewId);
+ return view != null ? view.getMeasuredHeight() : 0;
+ }
+
+ private int getDialogMarginPx() {
+ return getResources().getDimensionPixelSize(R.dimen.biometric_dialog_border_padding);
+ }
+
+ @NonNull
+ private static Insets getNavbarInsets(@Nullable WindowManager windowManager) {
+ return windowManager != null && windowManager.getCurrentWindowMetrics() != null
+ ? windowManager.getCurrentWindowMetrics().getWindowInsets()
+ .getInsets(WindowInsets.Type.navigationBars())
+ : Insets.NONE;
+ }
+
+ @NonNull
+ private static Rect getWindowBounds(@Nullable WindowManager windowManager) {
+ return windowManager != null && windowManager.getCurrentWindowMetrics() != null
+ ? windowManager.getCurrentWindowMetrics().getBounds()
+ : new Rect();
+ }
+
+ /**
+ * For devices in portrait orientation where the sensor is too high up, calculates the amount of
+ * padding necessary to center the biometric icon within the sensor's physical location.
+ */
+ @VisibleForTesting
+ static int calculateBottomSpacerHeightForPortrait(
+ @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
+ int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
+ int navbarBottomInsetPx) {
+
+ final int sensorDistanceFromBottom = displayHeightPx
+ - sensorProperties.sensorLocationY
+ - sensorProperties.sensorRadius;
+
+ final int spacerHeight = sensorDistanceFromBottom
+ - textIndicatorHeightPx
+ - buttonBarHeightPx
+ - dialogMarginPx
+ - navbarBottomInsetPx;
+
+ Log.d(TAG, "Display height: " + displayHeightPx
+ + ", Distance from bottom: " + sensorDistanceFromBottom
+ + ", Bottom margin: " + dialogMarginPx
+ + ", Navbar bottom inset: " + navbarBottomInsetPx
+ + ", Bottom spacer height (portrait): " + spacerHeight);
+
+ return spacerHeight;
+ }
+
+ /**
+ * For devices in landscape orientation where the sensor is too high up, calculates the amount
+ * of padding necessary to center the biometric icon within the sensor's physical location.
+ */
+ @VisibleForTesting
+ static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtitleHeightPx,
+ int descriptionHeightPx, int topSpacerHeightPx, int textIndicatorHeightPx,
+ int buttonBarHeightPx, int navbarBottomInsetPx) {
+
+ final int dialogHeightAboveIcon = titleHeightPx
+ + subtitleHeightPx
+ + descriptionHeightPx
+ + topSpacerHeightPx;
+
+ final int dialogHeightBelowIcon = textIndicatorHeightPx + buttonBarHeightPx;
+
+ final int bottomSpacerHeight = dialogHeightAboveIcon
+ - dialogHeightBelowIcon
+ - navbarBottomInsetPx;
+
+ Log.d(TAG, "Title height: " + titleHeightPx
+ + ", Subtitle height: " + subtitleHeightPx
+ + ", Description height: " + descriptionHeightPx
+ + ", Top spacer height: " + topSpacerHeightPx
+ + ", Text indicator height: " + textIndicatorHeightPx
+ + ", Button bar height: " + buttonBarHeightPx
+ + ", Navbar bottom inset: " + navbarBottomInsetPx
+ + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
+
+ return bottomSpacerHeight;
+ }
+
+ /**
+ * For devices in landscape orientation where the sensor is too left/right, calculates the
+ * amount of padding necessary to center the biometric icon within the sensor's physical
+ * location.
+ */
+ @VisibleForTesting
+ static int calculateHorizontalSpacerWidthForLandscape(
+ @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
+ int dialogMarginPx, int navbarHorizontalInsetPx) {
+
+ final int sensorDistanceFromEdge = displayWidthPx
+ - sensorProperties.sensorLocationY
+ - sensorProperties.sensorRadius;
+
+ final int horizontalPadding = sensorDistanceFromEdge
+ - dialogMarginPx
+ - navbarHorizontalInsetPx;
+
+ Log.d(TAG, "Display width: " + displayWidthPx
+ + ", Distance from edge: " + sensorDistanceFromEdge
+ + ", Dialog margin: " + dialogMarginPx
+ + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
+ + ", Horizontal spacer width (landscape): " + horizontalPadding);
+
+ return horizontalPadding;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 18206efefe9a..d59a865e2add 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -742,6 +742,7 @@ public abstract class AuthBiometricView extends LinearLayout {
* @param height Height to constrain the measurements to.
* @return See {@link AuthDialog.LayoutParams}
*/
+ @NonNull
AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
int totalHeight = 0;
final int numChildren = getChildCount();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 935f89343754..d05e9278762d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -32,8 +32,10 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserManager;
import android.util.Log;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -433,6 +435,29 @@ public class AuthContainerView extends LinearLayout
+ mConfig.mPromptInfo.getAuthenticators());
}
+ if (mBiometricView instanceof AuthBiometricUdfpsView) {
+ final int displayRotation = getDisplay().getRotation();
+ switch (displayRotation) {
+ case Surface.ROTATION_0:
+ mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
+ setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ break;
+ case Surface.ROTATION_90:
+ mPanelController.setPosition(AuthPanelController.POSITION_RIGHT);
+ setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ break;
+ case Surface.ROTATION_270:
+ mPanelController.setPosition(AuthPanelController.POSITION_LEFT);
+ setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ break;
+ default:
+ Log.e(TAG, "Unsupported display rotation: " + displayRotation);
+ mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
+ setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ break;
+ }
+ }
+
if (mConfig.mSkipIntro) {
mContainerState = STATE_SHOWING;
} else {
@@ -476,6 +501,13 @@ public class AuthContainerView extends LinearLayout
}
}
+ private void setScrollViewGravity(int gravity) {
+ final FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) mBiometricScrollView.getLayoutParams();
+ params.gravity = gravity;
+ mBiometricScrollView.setLayoutParams(params);
+ }
+
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index 11503fbd0b1d..fa50f895f83e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Outline;
import android.util.Log;
@@ -27,10 +28,20 @@ import android.view.animation.AccelerateDecelerateInterpolator;
import com.android.systemui.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Controls the back panel and its animations for the BiometricPrompt UI.
*/
public class AuthPanelController extends ViewOutlineProvider {
+ public static final int POSITION_BOTTOM = 1;
+ public static final int POSITION_LEFT = 2;
+ public static final int POSITION_RIGHT = 3;
+
+ @IntDef({POSITION_BOTTOM, POSITION_LEFT, POSITION_RIGHT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Position {}
private static final String TAG = "BiometricPrompt/AuthPanelController";
private static final boolean DEBUG = false;
@@ -38,6 +49,7 @@ public class AuthPanelController extends ViewOutlineProvider {
private final Context mContext;
private final View mPanelView;
+ @Position private int mPosition = POSITION_BOTTOM;
private boolean mUseFullScreen;
private int mContainerWidth;
@@ -51,21 +63,44 @@ public class AuthPanelController extends ViewOutlineProvider {
@Override
public void getOutline(View view, Outline outline) {
- final int left = (mContainerWidth - mContentWidth) / 2;
- final int right = mContainerWidth - left;
-
- // If the content fits within the container, shrink the height to wrap the content.
- // Otherwise, set the outline to be the display size minus the margin - the content within
- // is scrollable.
- final int top = mContentHeight < mContainerHeight
- ? mContainerHeight - mContentHeight - mMargin
- : mMargin;
-
- // TODO(b/139954942) Likely don't need to "+1" after we resolve the navbar styling.
- final int bottom = mContainerHeight - mMargin + 1;
+ final int left = getLeftBound(mPosition);
+ final int right = left + mContentWidth;
+
+ // If the content fits in the container, shrink the height to wrap it. Otherwise, expand to
+ // fill the display (minus the margin), since the content is scrollable.
+ final int top = getTopBound(mPosition);
+ final int bottom = Math.min(top + mContentHeight, mContainerHeight - mMargin);
+
outline.setRoundRect(left, top, right, bottom, mCornerRadius);
}
+ private int getLeftBound(@Position int position) {
+ switch (position) {
+ case POSITION_BOTTOM:
+ return (mContainerWidth - mContentWidth) / 2;
+ case POSITION_LEFT:
+ return mMargin;
+ case POSITION_RIGHT:
+ return mContainerWidth - mContentWidth - mMargin;
+ default:
+ Log.e(TAG, "Unrecognized position: " + position);
+ return getLeftBound(POSITION_BOTTOM);
+ }
+ }
+
+ private int getTopBound(@Position int position) {
+ switch (position) {
+ case POSITION_BOTTOM:
+ return Math.max(mContainerHeight - mContentHeight - mMargin, mMargin);
+ case POSITION_LEFT:
+ case POSITION_RIGHT:
+ return Math.max((mContainerHeight - mContentHeight) / 2, mMargin);
+ default:
+ Log.e(TAG, "Unrecognized position: " + position);
+ return getTopBound(POSITION_BOTTOM);
+ }
+ }
+
public void setContainerDimensions(int containerWidth, int containerHeight) {
if (DEBUG) {
Log.v(TAG, "Container Width: " + containerWidth + " Height: " + containerHeight);
@@ -74,6 +109,10 @@ public class AuthPanelController extends ViewOutlineProvider {
mContainerHeight = containerHeight;
}
+ public void setPosition(@Position int position) {
+ mPosition = position;
+ }
+
public void setUseFullScreen(boolean fullScreen) {
mUseFullScreen = fullScreen;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 43ecf6778022..60fdbab8482c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -16,141 +16,94 @@
package com.android.systemui.biometrics;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.FrameLayout;
-import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.statusbar.phone.StatusBar;
-
/**
* Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
- * can support multiple child views drawing on the same region around the sensor location.
+ * can support multiple child views drawing in the same region around the sensor location.
+ *
+ * - hides animation view when pausing auth
+ * - sends illumination events to fingerprint drawable
+ * - sends sensor rect updates to fingerprint drawable
+ * - optionally can override dozeTimeTick to adjust views for burn-in mitigation
*/
-public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver,
- StatusBar.ExpansionChangedListener {
-
- private static final String TAG = "UdfpsAnimationView";
+abstract class UdfpsAnimationView extends FrameLayout {
- @Nullable protected abstract UdfpsAnimation getUdfpsAnimation();
-
- @NonNull private UdfpsView mParent;
- @NonNull private RectF mSensorRect;
private int mAlpha;
+ private boolean mPauseAuth;
public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mSensorRect = new RectF();
- setWillNotDraw(false);
}
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
+ /**
+ * Fingerprint drawable
+ */
+ abstract UdfpsDrawable getDrawable();
- if (getUdfpsAnimation() != null) {
- final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
- getUdfpsAnimation().setAlpha(alpha);
- getUdfpsAnimation().draw(canvas);
- }
+ void onSensorRectUpdated(RectF bounds) {
+ getDrawable().onSensorRectUpdated(bounds);
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- if (getUdfpsAnimation() != null) {
- getUdfpsAnimation().onDestroy();
- }
+ void onIlluminationStarting() {
+ getDrawable().setIlluminationShowing(true);
+ getDrawable().invalidateSelf();
}
- private int expansionToAlpha(float expansion) {
- // Fade to 0 opacity when reaching this expansion amount
- final float maxExpansion = 0.4f;
-
- if (expansion >= maxExpansion) {
- return 0; // transparent
- }
-
- final float percent = expansion / maxExpansion;
- return (int) ((1 - percent) * 255);
+ void onIlluminationStopped() {
+ getDrawable().setIlluminationShowing(false);
+ getDrawable().invalidateSelf();
}
- void onIlluminationStarting() {
- if (getUdfpsAnimation() == null) {
- return;
+ /**
+ * @return true if changed
+ */
+ boolean setPauseAuth(boolean pauseAuth) {
+ if (pauseAuth != mPauseAuth) {
+ mPauseAuth = pauseAuth;
+ updateAlpha();
+ return true;
}
-
- getUdfpsAnimation().setIlluminationShowing(true);
- postInvalidate();
+ return false;
}
- void onIlluminationStopped() {
- if (getUdfpsAnimation() == null) {
- return;
- }
-
- getUdfpsAnimation().setIlluminationShowing(false);
- postInvalidate();
+ protected void updateAlpha() {
+ getDrawable().setAlpha(calculateAlpha());
}
- void setParent(@NonNull UdfpsView parent) {
- mParent = parent;
+ protected final int calculateAlpha() {
+ return mPauseAuth ? mAlpha : 255;
}
- void onSensorRectUpdated(@NonNull RectF sensorRect) {
- mSensorRect = sensorRect;
- if (getUdfpsAnimation() != null) {
- getUdfpsAnimation().onSensorRectUpdated(mSensorRect);
- }
+ boolean isPauseAuth() {
+ return mPauseAuth;
}
- void updateColor() {
- if (getUdfpsAnimation() != null) {
- getUdfpsAnimation().updateColor();
- }
- postInvalidate();
- }
+ private int expansionToAlpha(float expansion) {
+ // Fade to 0 opacity when reaching this expansion amount
+ final float maxExpansion = 0.4f;
- @Override
- public void dozeTimeTick() {
- if (getUdfpsAnimation() instanceof DozeReceiver) {
- ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick();
+ if (expansion >= maxExpansion) {
+ return 0; // transparent
}
+
+ final float percent = expansion / maxExpansion;
+ return (int) ((1 - percent) * 255);
}
- @Override
public void onExpansionChanged(float expansion, boolean expanded) {
mAlpha = expansionToAlpha(expansion);
- postInvalidate();
- }
-
- public int getPaddingX() {
- if (getUdfpsAnimation() == null) {
- return 0;
- }
- return getUdfpsAnimation().getPaddingX();
- }
-
- public int getPaddingY() {
- if (getUdfpsAnimation() == null) {
- return 0;
- }
- return getUdfpsAnimation().getPaddingY();
+ updateAlpha();
}
/**
- * @return the amount of translation needed if the view currently requires the user to touch
- * somewhere other than the exact center of the sensor. For example, this can happen
- * during guided enrollment.
+ * @return true if handled
*/
- @NonNull
- PointF getTouchTranslation() {
- return new PointF(0, 0);
+ boolean dozeTimeTick() {
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
new file mode 100644
index 000000000000..b7726f41e4a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.ViewController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Handles:
+ * 1. registering for listeners when its view is attached and unregistering on view detached
+ * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
+ * 3. sending events to its view including:
+ * - illumination events
+ * - sensor position changes
+ * - doze time event
+ */
+abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
+ extends ViewController<T> implements Dumpable {
+ @NonNull final StatusBarStateController mStatusBarStateController;
+ @NonNull final StatusBar mStatusBar;
+ @NonNull final DumpManager mDumpManger;
+
+ private boolean mNotificationShadeExpanded;
+ private int mStatusBarState;
+
+ protected UdfpsAnimationViewController(
+ T view,
+ @NonNull StatusBarStateController statusBarStateController,
+ @NonNull StatusBar statusBar,
+ @NonNull DumpManager dumpManager) {
+ super(view);
+ mStatusBarStateController = statusBarStateController;
+ mStatusBar = statusBar;
+ mDumpManger = dumpManager;
+ }
+
+ abstract @NonNull String getTag();
+
+ @Override
+ protected void onViewAttached() {
+ mStatusBarStateController.addCallback(mStateListener);
+ mStateListener.onStateChanged(mStatusBarStateController.getState());
+ mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+
+ mDumpManger.registerDumpable(getTag(), this);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mStatusBarStateController.removeCallback(mStateListener);
+ mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+
+ mDumpManger.unregisterDumpable(getTag());
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState));
+ pw.println("mNotificationShadeExpanded=" + mNotificationShadeExpanded);
+ pw.println("shouldPauseAuth()=" + shouldPauseAuth());
+ pw.println("isPauseAuth=" + mView.isPauseAuth());
+ }
+
+ /**
+ * Returns true if the fingerprint manager is running but we want to temporarily pause
+ * authentication.
+ */
+ boolean shouldPauseAuth() {
+ return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
+ || mStatusBarState == SHADE_LOCKED
+ || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+ }
+
+ /**
+ * Send pause auth update to our view.
+ */
+ void updatePauseAuth() {
+ if (mView.setPauseAuth(shouldPauseAuth())) {
+ mView.postInvalidate();
+ }
+ }
+
+ /**
+ * Send sensor position change to our view. This rect contains paddingX and paddingY.
+ */
+ void onSensorRectUpdated(RectF sensorRect) {
+ mView.onSensorRectUpdated(sensorRect);
+ }
+
+ /**
+ * Send dozeTimeTick to view in case it wants to handle its burn-in offset.
+ */
+ void dozeTimeTick() {
+ if (mView.dozeTimeTick()) {
+ mView.postInvalidate();
+ }
+ }
+
+ /**
+ * @return the amount of translation needed if the view currently requires the user to touch
+ * somewhere other than the exact center of the sensor. For example, this can happen
+ * during guided enrollment.
+ */
+ PointF getTouchTranslation() {
+ return new PointF(0, 0);
+ }
+
+ /**
+ * X-Padding to add to left and right of the sensor rectangle area to increase the size of our
+ * window to draw within.
+ * @return
+ */
+ int getPaddingX() {
+ return 0;
+ }
+
+ /**
+ * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our
+ * window to draw within.
+ */
+ int getPaddingY() {
+ return 0;
+ }
+
+ /**
+ * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+ */
+ void onIlluminationStarting() {
+ mView.onIlluminationStarting();
+ mView.postInvalidate();
+ }
+
+ /**
+ * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
+ * authenticate.
+ */
+ void onIlluminationStopped() {
+ mView.onIlluminationStopped();
+ mView.postInvalidate();
+ }
+
+ private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
+ new StatusBar.ExpansionChangedListener() {
+ @Override
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mNotificationShadeExpanded = expanded;
+ mView.onExpansionChanged(expansion, expanded);
+ updatePauseAuth();
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ mStatusBarState = newState;
+ updatePauseAuth();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
deleted file mode 100644
index 543df33dd5d7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * Class that coordinates non-HBM animations during enrollment.
- */
-public class UdfpsAnimationViewEnroll extends UdfpsAnimationView
- implements UdfpsEnrollHelper.Listener {
-
- private static final String TAG = "UdfpsAnimationViewEnroll";
-
- @NonNull private UdfpsAnimationEnroll mUdfpsAnimation;
- @NonNull private UdfpsProgressBar mProgressBar;
- @Nullable private UdfpsEnrollHelper mEnrollHelper;
-
- @NonNull
- @Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return mUdfpsAnimation;
- }
-
- public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mUdfpsAnimation = new UdfpsAnimationEnroll(context);
- }
-
- public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
- mEnrollHelper = helper;
- mUdfpsAnimation.setEnrollHelper(helper);
- }
-
- @Override
- protected void onFinishInflate() {
- mProgressBar = findViewById(R.id.progress_bar);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mEnrollHelper == null) {
- Log.e(TAG, "Enroll helper is null");
- return;
- }
-
- if (mEnrollHelper.shouldShowProgressBar()) {
- mProgressBar.setVisibility(View.VISIBLE);
-
- // Only need enrollment updates if the progress bar is showing :)
- mEnrollHelper.setListener(this);
- }
- }
-
- @Override
- public void onEnrollmentProgress(int remaining, int totalSteps) {
- final int interpolatedProgress = mProgressBar.getMax()
- * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
-
- mProgressBar.setProgress(interpolatedProgress, true);
- }
-
- @NonNull
- @Override
- PointF getTouchTranslation() {
- if (!mEnrollHelper.isCenterEnrollmentComplete()) {
- return new PointF(0, 0);
- } else {
- return mEnrollHelper.getNextGuidedEnrollmentPoint();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
deleted file mode 100644
index 7d0b3e59feb1..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-
-/**
- * Class that coordinates non-HBM animations during keyguard authentication.
- */
-public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView {
- @Nullable private UdfpsAnimationKeyguard mAnimation;
-
- public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) {
- if (mAnimation == null) {
- mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController);
- mAnimation.setAnimationView(this);
- }
- }
-
- @Nullable
- @Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return mAnimation;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
index 515b442b61f6..70be907228c8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
@@ -24,19 +24,22 @@ import androidx.annotation.Nullable;
/**
* Class that coordinates non-HBM animations during BiometricPrompt.
*
+ * Currently doesn't draw anything.
+ *
* Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should
- * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController
- * is completed (inflate operation-specific views, instead of inflating generic udfps_view and
- * adding operation-specific animations to it).
+ * de-dupe this if necessary.
*/
-public class UdfpsAnimationViewBp extends UdfpsAnimationView {
- public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) {
+public class UdfpsBpView extends UdfpsAnimationView {
+ private UdfpsFpDrawable mFingerprintDrawable;
+
+ public UdfpsBpView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ // Drawable isn't ever added to the view, so we don't currently show anything
+ mFingerprintDrawable = new UdfpsFpDrawable(mContext);
}
- @Nullable
@Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return null;
+ UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
new file mode 100644
index 000000000000..93d80e29aded
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for biometric prompt.
+ */
+class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
+ protected UdfpsBpViewController(
+ @NonNull UdfpsBpView view,
+ @NonNull StatusBarStateController statusBarStateController,
+ @NonNull StatusBar statusBar,
+ @NonNull DumpManager dumpManager) {
+ super(view, statusBarStateController, statusBar, dumpManager);
+ }
+
+ @Override
+ @NonNull String getTag() {
+ return "UdfpsBpViewController";
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 1d789ca15dfb..797a4411b8c9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -31,9 +31,9 @@ import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.SystemClock;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -50,8 +50,10 @@ import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
import javax.inject.Inject;
@@ -83,6 +85,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
private final DelayableExecutor mFgExecutor;
@NonNull private final StatusBar mStatusBar;
@NonNull private final StatusBarStateController mStatusBarStateController;
+ @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
+ @NonNull private final DumpManager mDumpManager;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -195,17 +199,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
}
}
- @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
- (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);
-
- @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onStateChanged(int newState) {
- mView.onStateChanged(newState);
- }
- };
-
private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
final float vx = tracker.getXVelocity(pointerId);
final float vy = tracker.getYVelocity(pointerId);
@@ -310,7 +303,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull StatusBar statusBar) {
+ @NonNull StatusBar statusBar,
+ @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ @NonNull DumpManager dumpManager) {
mContext = context;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -320,6 +315,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mFgExecutor = fgExecutor;
mStatusBar = statusBar;
mStatusBarStateController = statusBarStateController;
+ mKeyguardViewManager = statusBarKeyguardViewManager;
+ mDumpManager = dumpManager;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -360,10 +357,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@Override
public void dozeTimeTick() {
- if (mView == null) {
- return;
+ if (mView != null) {
+ mView.dozeTimeTick();
}
- mView.dozeTimeTick();
}
/**
@@ -387,7 +383,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
}
}
- private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) {
+ private WindowManager.LayoutParams computeLayoutParams(
+ @Nullable UdfpsAnimationViewController animation) {
final int paddingX = animation != null ? animation.getPaddingX() : 0;
final int paddingY = animation != null ? animation.getPaddingY() : 0;
@@ -438,19 +435,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mFgExecutor.execute(() -> {
if (mView == null) {
try {
- Log.v(TAG, "showUdfpsOverlay | adding window");
- // TODO: Eventually we should refactor the code to inflate an
- // operation-specific view here, instead of inflating a generic udfps_view
- // and adding operation-specific animations to it.
+ Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
mView.setHbmCallback(this);
-
- final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason);
- mView.setAnimationView(animation);
-
- mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
+ UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
+ animation.init();
+ mView.setAnimationViewController(animation);
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
@@ -463,40 +454,51 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
});
}
- @NonNull
- private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) {
- Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
-
- final LayoutInflater inflater = LayoutInflater.from(mContext);
-
+ private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
switch (reason) {
case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
- case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
- final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll)
- inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
- view.setEnrollHelper(mServerRequest.mEnrollHelper);
- return view;
- }
-
- case IUdfpsOverlayController.REASON_AUTH_BP: {
- final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp)
- inflater.inflate(R.layout.udfps_animation_view_bp, null, false);
- return view;
- }
-
- case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
- final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard)
- inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
- view.setStatusBarStateController(mStatusBarStateController);
- return view;
- }
-
- case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
- final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther)
- inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
- return view;
- }
-
+ case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
+ UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate(
+ R.layout.udfps_enroll_view, null);
+ mView.addView(enrollView);
+ return new UdfpsEnrollViewController(
+ enrollView,
+ mServerRequest.mEnrollHelper,
+ mStatusBarStateController,
+ mStatusBar,
+ mDumpManager
+ );
+ case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
+ UdfpsKeyguardView keyguardView = (UdfpsKeyguardView)
+ mInflater.inflate(R.layout.udfps_keyguard_view, null);
+ mView.addView(keyguardView);
+ return new UdfpsKeyguardViewController(
+ keyguardView,
+ mStatusBarStateController,
+ mStatusBar,
+ mKeyguardViewManager,
+ mDumpManager
+ );
+ case IUdfpsOverlayController.REASON_AUTH_BP:
+ // note: empty controller, currently shows no visual affordance
+ UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null);
+ mView.addView(bpView);
+ return new UdfpsBpViewController(
+ bpView,
+ mStatusBarStateController,
+ mStatusBar,
+ mDumpManager
+ );
+ case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
+ UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
+ mInflater.inflate(R.layout.udfps_fpm_other_view, null);
+ mView.addView(authOtherView);
+ return new UdfpsFpmOtherViewController(
+ authOtherView,
+ mStatusBarStateController,
+ mStatusBar,
+ mDumpManager
+ );
default:
Log.d(TAG, "Animation for reason " + reason + " not supported yet");
return null;
@@ -509,11 +511,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.v(TAG, "hideUdfpsOverlay | removing window");
// Reset the controller back to its starting state.
onFingerUp();
-
- mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener);
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
-
mWindowManager.removeView(mView);
+ mView.setOnTouchListener(null);
+ mView.setAnimationViewController(null);
mView = null;
} else {
Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
index a51b6fd16445..18f54166ad28 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
@@ -17,10 +17,10 @@
package com.android.systemui.biometrics;
import android.content.Context;
+import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,24 +28,24 @@ import androidx.annotation.Nullable;
import com.android.systemui.R;
/**
- * Abstract base class for animations that should be drawn when the finger is not touching the
+ * Abstract base class for drawable displayed when the finger is not touching the
* sensor area.
*/
-public abstract class UdfpsAnimation extends Drawable {
- protected abstract void updateColor();
- protected abstract void onDestroy();
-
+public abstract class UdfpsDrawable extends Drawable {
@NonNull protected final Context mContext;
@NonNull protected final Drawable mFingerprintDrawable;
- @Nullable private View mView;
private boolean mIlluminationShowing;
- public UdfpsAnimation(@NonNull Context context) {
+ int mAlpha = 255; // 0 - 255
+ public UdfpsDrawable(@NonNull Context context) {
mContext = context;
mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
mFingerprintDrawable.mutate();
}
+ /**
+ * @param sensorRect the rect coordinates for the sensor area
+ */
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
final int margin = (int) sensorRect.height() / 8;
@@ -56,17 +56,19 @@ public abstract class UdfpsAnimation extends Drawable {
updateFingerprintIconBounds(bounds);
}
+ /**
+ * Bounds for the fingerprint icon
+ */
protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
mFingerprintDrawable.setBounds(bounds);
+ invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
- mFingerprintDrawable.setAlpha(alpha);
- }
-
- public void setAnimationView(UdfpsAnimationView view) {
- mView = view;
+ mAlpha = alpha;
+ mFingerprintDrawable.setAlpha(mAlpha);
+ invalidateSelf();
}
boolean isIlluminationShowing() {
@@ -74,26 +76,19 @@ public abstract class UdfpsAnimation extends Drawable {
}
void setIlluminationShowing(boolean showing) {
+ if (mIlluminationShowing == showing) {
+ return;
+ }
mIlluminationShowing = showing;
+ invalidateSelf();
}
- /**
- * @return The amount of padding that's needed on each side of the sensor, in pixels.
- */
- public int getPaddingX() {
- return 0;
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
- /**
- * @return The amount of padding that's needed on each side of the sensor, in pixels.
- */
- public int getPaddingY() {
+ @Override
+ public int getOpacity() {
return 0;
}
-
- protected void postInvalidateView() {
- if (mView != null) {
- mView.postInvalidate();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 015a598e972b..cd5abd74c260 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -17,10 +17,8 @@
package com.android.systemui.biometrics;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -33,30 +31,29 @@ import androidx.annotation.Nullable;
import com.android.systemui.R;
/**
- * UDFPS animations that should be shown when enrolling.
+ * UDFPS fingerprint drawable that is shown when enrolling
*/
-public class UdfpsAnimationEnroll extends UdfpsAnimation {
+public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
- private static final float SHADOW_RADIUS = 5.f;
- private static final float PROGRESS_BAR_RADIUS = 140.f;
+ static final float PROGRESS_BAR_RADIUS = 180.f;
@NonNull private final Drawable mMovingTargetFpIcon;
- @NonNull private final Paint mSensorPaint;
+ @NonNull private final Paint mSensorOutlinePaint;
@NonNull private final Paint mBlueFill;
- @NonNull private final Paint mBlueStroke;;
+ @NonNull private final Paint mBlueStroke;
@Nullable private RectF mSensorRect;
@Nullable private UdfpsEnrollHelper mEnrollHelper;
- UdfpsAnimationEnroll(@NonNull Context context) {
+ UdfpsEnrollDrawable(@NonNull Context context) {
super(context);
- mSensorPaint = new Paint(0 /* flags */);
- mSensorPaint.setAntiAlias(true);
- mSensorPaint.setColor(Color.WHITE);
- mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK);
- mSensorPaint.setStyle(Paint.Style.FILL);
+ mSensorOutlinePaint = new Paint(0 /* flags */);
+ mSensorOutlinePaint.setAntiAlias(true);
+ mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
+ mSensorOutlinePaint.setStyle(Paint.Style.STROKE);
+ mSensorOutlinePaint.setStrokeWidth(2.f);
mBlueFill = new Paint(0 /* flags */);
mBlueFill.setAntiAlias(true);
@@ -72,20 +69,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
mMovingTargetFpIcon.setTint(Color.WHITE);
mMovingTargetFpIcon.mutate();
- }
- void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
- mEnrollHelper = helper;
- }
-
- @Override
- protected void updateColor() {
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
}
- @Override
- protected void onDestroy() {
-
+ void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
+ mEnrollHelper = helper;
}
@Override
@@ -98,6 +87,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
super.updateFingerprintIconBounds(bounds);
mMovingTargetFpIcon.setBounds(bounds);
+ invalidateSelf();
}
@Override
@@ -106,18 +96,15 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
return;
}
- final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_YES) != 0;
- if (!isNightMode) {
- if (mSensorRect != null) {
- canvas.drawOval(mSensorRect, mSensorPaint);
- }
+ if (mSensorRect != null) {
+ canvas.drawOval(mSensorRect, mSensorOutlinePaint);
}
mFingerprintDrawable.draw(canvas);
// Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) {
- mFingerprintDrawable.setAlpha(64);
+ mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
+ mSensorOutlinePaint.setAlpha(mAlpha == 255 ? 64 : mAlpha);
canvas.save();
final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
@@ -130,33 +117,18 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
mMovingTargetFpIcon.draw(canvas);
canvas.restore();
} else {
- mFingerprintDrawable.setAlpha(255);
+ mFingerprintDrawable.setAlpha(mAlpha);
+ mSensorOutlinePaint.setAlpha(mAlpha);
}
}
@Override
- public int getPaddingX() {
- return (int) Math.ceil(PROGRESS_BAR_RADIUS);
- }
-
- @Override
- public int getPaddingY() {
- return (int) Math.ceil(PROGRESS_BAR_RADIUS);
- }
-
- @Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
- mSensorPaint.setAlpha(alpha);
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
+ mSensorOutlinePaint.setAlpha(alpha);
+ mBlueFill.setAlpha(alpha);
+ mBlueStroke.setAlpha(alpha);
+ mMovingTargetFpIcon.setAlpha(alpha);
+ invalidateSelf();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 14eca1b1cb2c..98a703f595d2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -21,6 +21,9 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.TypedValue;
import java.util.ArrayList;
@@ -32,6 +35,10 @@ import java.util.List;
public class UdfpsEnrollHelper {
private static final String TAG = "UdfpsEnrollHelper";
+ private static final String SCALE_OVERRIDE =
+ "com.android.systemui.biometrics.UdfpsEnrollHelper.scale";
+ private static final float SCALE = 0.5f;
+
// Enroll with two center touches before going to guided enrollment
private static final int NUM_CENTER_TOUCHES = 2;
@@ -39,9 +46,10 @@ public class UdfpsEnrollHelper {
void onEnrollmentProgress(int remaining, int totalSteps);
}
+ @NonNull private final Context mContext;
// IUdfpsOverlayController reason
private final int mEnrollReason;
- private final List<PointF> mGuidedEnrollmentPoints;
+ @NonNull private final List<PointF> mGuidedEnrollmentPoints;
private int mTotalSteps = -1;
private int mRemainingSteps = -1;
@@ -53,6 +61,7 @@ public class UdfpsEnrollHelper {
@Nullable Listener mListener;
public UdfpsEnrollHelper(@NonNull Context context, int reason) {
+ mContext = context;
mEnrollReason = reason;
mGuidedEnrollmentPoints = new ArrayList<>();
@@ -100,13 +109,13 @@ public class UdfpsEnrollHelper {
}
- void setListener(@NonNull Listener listener) {
+ void setListener(Listener listener) {
mListener = listener;
// Only notify during setListener if enrollment is already in progress, so the progress
// bar can be updated. If enrollment has not started yet, the progress bar will be empty
// anyway.
- if (mTotalSteps != -1) {
+ if (mListener != null && mTotalSteps != -1) {
mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
}
}
@@ -121,7 +130,15 @@ public class UdfpsEnrollHelper {
@NonNull
PointF getNextGuidedEnrollmentPoint() {
+ float scale = SCALE;
+ if (Build.IS_ENG || Build.IS_USERDEBUG) {
+ scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+ SCALE_OVERRIDE, SCALE,
+ UserHandle.USER_CURRENT);
+ }
final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
- return mGuidedEnrollmentPoints.get(index % mGuidedEnrollmentPoints.size());
+ final PointF originalPoint = mGuidedEnrollmentPoints
+ .get(index % mGuidedEnrollmentPoints.size());
+ return new PointF(originalPoint.x * scale, originalPoint.y * scale);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
new file mode 100644
index 000000000000..75e8638e43df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_enroll_view.xml
+ */
+public class UdfpsEnrollView extends UdfpsAnimationView {
+ @NonNull private final UdfpsEnrollDrawable mFingerprintDrawable;
+ @NonNull private ImageView mFingerprintView;
+ @NonNull private UdfpsProgressBar mProgressBar;
+
+ public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mFingerprintDrawable = new UdfpsEnrollDrawable(mContext);
+ }
+
+ @Override
+ protected void updateAlpha() {
+ super.updateAlpha();
+ mProgressBar.setAlpha(calculateAlpha());
+ mProgressBar.getProgressDrawable().setAlpha(calculateAlpha());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view);
+ mFingerprintView.setImageDrawable(mFingerprintDrawable);
+ mProgressBar = findViewById(R.id.progress_bar);
+ }
+
+ @Override
+ public UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
+ }
+
+ void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
+ mFingerprintDrawable.setEnrollHelper(enrollHelper);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
new file mode 100644
index 000000000000..1ebbfbf84814
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during enrollment.
+ */
+public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> {
+ @NonNull private final UdfpsProgressBar mProgressBar;
+ @NonNull private final UdfpsEnrollHelper mEnrollHelper;
+
+ protected UdfpsEnrollViewController(
+ @NonNull UdfpsEnrollView view,
+ @NonNull UdfpsEnrollHelper enrollHelper,
+ @NonNull StatusBarStateController statusBarStateController,
+ @NonNull StatusBar statusBar,
+ @NonNull DumpManager dumpManager) {
+ super(view, statusBarStateController, statusBar, dumpManager);
+ mEnrollHelper = enrollHelper;
+ mProgressBar = mView.findViewById(R.id.progress_bar);
+ mView.setEnrollHelper(mEnrollHelper);
+ }
+
+ @Override
+ @NonNull String getTag() {
+ return "UdfpsEnrollViewController";
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ if (mEnrollHelper.shouldShowProgressBar()) {
+ mProgressBar.setVisibility(View.VISIBLE);
+
+ // Only need enrollment updates if the progress bar is showing :)
+ mEnrollHelper.setListener(mEnrollHelperListener);
+ }
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mEnrollHelper.setListener(null);
+ }
+
+ @NonNull
+ @Override
+ public PointF getTouchTranslation() {
+ if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+ return new PointF(0, 0);
+ } else {
+ return mEnrollHelper.getNextGuidedEnrollmentPoint();
+ }
+ }
+
+ @Override
+ public int getPaddingX() {
+ return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+ }
+
+ @Override
+ public int getPaddingY() {
+ return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+ }
+
+ private final UdfpsEnrollHelper.Listener mEnrollHelperListener =
+ new UdfpsEnrollHelper.Listener() {
+ @Override
+ public void onEnrollmentProgress(int remaining, int totalSteps) {
+ final int interpolatedProgress = mProgressBar.getMax()
+ * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
+
+ mProgressBar.setProgress(interpolatedProgress, true);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
index ef7a34000841..09b6fabbdd15 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
@@ -18,32 +18,19 @@ package com.android.systemui.biometrics;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.ColorFilter;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
/**
- * UDFPS animations that should be shown when authenticating via FingerprintManager, excluding
- * keyguard.
+ * Draws udfps fingerprint if sensor isn't illuminating.
*/
-public class UdfpsAnimationFpmOther extends UdfpsAnimation {
+public class UdfpsFpDrawable extends UdfpsDrawable {
- UdfpsAnimationFpmOther(@NonNull Context context) {
+ UdfpsFpDrawable(@NonNull Context context) {
super(context);
}
@Override
- protected void updateColor() {
-
- }
-
- @Override
- protected void onDestroy() {
-
- }
-
- @Override
public void draw(@NonNull Canvas canvas) {
if (isIlluminationShowing()) {
return;
@@ -51,14 +38,4 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation {
mFingerprintDrawable.draw(canvas);
}
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
index 3d2f5a0fe5cf..85f16068188e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
@@ -18,25 +18,32 @@ package com.android.systemui.biometrics;
import android.content.Context;
import android.util.AttributeSet;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
+import com.android.systemui.R;
+
/**
- * Class that coordinates non-HBM animations during other usage of FingerprintManager (not
- * including Keyguard).
+ * View corresponding with udfps_fpm_other_view.xml
*/
-public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView {
-
- private final UdfpsAnimationFpmOther mAnimation;
+public class UdfpsFpmOtherView extends UdfpsAnimationView {
+ private final UdfpsFpDrawable mFingerprintDrawable;
+ private ImageView mFingerprintView;
- public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) {
+ public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mAnimation = new UdfpsAnimationFpmOther(context);
+ mFingerprintDrawable = new UdfpsFpDrawable(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view);
+ mFingerprintView.setImageDrawable(mFingerprintDrawable);
}
- @Nullable
@Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return mAnimation;
+ UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
new file mode 100644
index 000000000000..6e2e4baf492b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
+ * states.
+ *
+ * Currently only shows the fp drawable.
+ */
+class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> {
+ protected UdfpsFpmOtherViewController(
+ @NonNull UdfpsFpmOtherView view,
+ @NonNull StatusBarStateController statusBarStateController,
+ @NonNull StatusBar statusBar,
+ @NonNull DumpManager dumpManager) {
+ super(view, statusBarStateController, statusBar, dumpManager);
+ }
+
+ @Override
+ @NonNull String getTag() {
+ return "UdfpsFpmOtherViewController";
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
index 5f268cfa8fa5..12c15a0882f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
@@ -21,48 +21,46 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.util.MathUtils;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
/**
* UDFPS animations that should be shown when authenticating on keyguard.
*/
-public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiver,
- StatusBarStateController.StateListener {
+public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver {
private static final String TAG = "UdfpsAnimationKeyguard";
+ private final int mAmbientDisplayColor;
@NonNull private final Context mContext;
- @NonNull private final StatusBarStateController mStatusBarStateController;
- private final int mMaxBurnInOffsetX;
- private final int mMaxBurnInOffsetY;
+ private int mLockScreenColor;
// AOD anti-burn-in offsets
+ private final int mMaxBurnInOffsetX;
+ private final int mMaxBurnInOffsetY;
private float mInterpolatedDarkAmount;
private float mBurnInOffsetX;
private float mBurnInOffsetY;
- UdfpsAnimationKeyguard(@NonNull Context context,
- @NonNull StatusBarStateController statusBarStateController) {
+ UdfpsKeyguardDrawable(@NonNull Context context) {
super(context);
mContext = context;
- mStatusBarStateController = statusBarStateController;
+ // TODO: move burn-in to view
mMaxBurnInOffsetX = context.getResources()
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
mMaxBurnInOffsetY = context.getResources()
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
- statusBarStateController.addCallback(this);
+ mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+ mAmbientDisplayColor = Color.WHITE;
+ updateAodPositionAndColor();
}
private void updateAodPositionAndColor() {
@@ -74,18 +72,14 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- mMaxBurnInOffsetY,
mInterpolatedDarkAmount);
- updateColor();
- postInvalidateView();
- }
- @Override
- public void dozeTimeTick() {
- updateAodPositionAndColor();
+ mFingerprintDrawable.setTint(ColorUtils.blendARGB(mLockScreenColor,
+ mAmbientDisplayColor, mInterpolatedDarkAmount));
+ invalidateSelf();
}
@Override
- public void onDozeAmountChanged(float linear, float eased) {
- mInterpolatedDarkAmount = eased;
+ public void dozeTimeTick() {
updateAodPositionAndColor();
}
@@ -94,34 +88,17 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
if (isIlluminationShowing()) {
return;
}
-
- canvas.save();
- canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
mFingerprintDrawable.draw(canvas);
- canvas.restore();
}
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- @Override
- protected void updateColor() {
- final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
- R.attr.wallpaperTextColor);
- final int ambientDisplayIconColor = Color.WHITE;
- mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
- ambientDisplayIconColor, mInterpolatedDarkAmount));
+ void onDozeAmountChanged(float linear, float eased) {
+ mInterpolatedDarkAmount = eased;
+ updateAodPositionAndColor();
}
- @Override
- protected void onDestroy() {
- mStatusBarStateController.removeCallback(this);
+ void setLockScreenColor(int color) {
+ if (mLockScreenColor == color) return;
+ mLockScreenColor = color;
+ updateAodPositionAndColor();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
new file mode 100644
index 000000000000..378907c400d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarState;
+
+/**
+ * View corresponding with udfps_keyguard_view.xml
+ */
+public class UdfpsKeyguardView extends UdfpsAnimationView {
+ private final UdfpsKeyguardDrawable mFingerprintDrawable;
+ private ImageView mFingerprintView;
+ private int mWallpaperTexColor;
+ private int mStatusBarState;
+
+ // used when highlighting fp icon:
+ private int mTextColorPrimary;
+ private ImageView mBgProtection;
+
+ private AnimatorSet mAnimatorSet;
+
+ public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mFingerprintDrawable = new UdfpsKeyguardDrawable(mContext);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view);
+ mFingerprintView.setForeground(mFingerprintDrawable);
+
+ mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
+
+ mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+ mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorPrimary);
+ }
+
+ @Override
+ public UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
+ }
+
+ @Override
+ void onIlluminationStarting() {
+ setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ void onIlluminationStopped() {
+ setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public boolean dozeTimeTick() {
+ // TODO: burnin
+ mFingerprintDrawable.dozeTimeTick();
+ return true;
+ }
+
+ void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
+ if (!isShadeLocked()) {
+ mFingerprintView.setAlpha(1f);
+ mFingerprintDrawable.setLockScreenColor(mWallpaperTexColor);
+ }
+ }
+
+ void onDozeAmountChanged(float linear, float eased) {
+ mFingerprintDrawable.onDozeAmountChanged(linear, eased);
+ invalidate();
+ }
+
+ /**
+ * Animates in the bg protection circle behind the fp icon to highlight the icon.
+ */
+ void animateHighlightFp() {
+ if (mBgProtection.getVisibility() == View.VISIBLE && mBgProtection.getAlpha() == 1f) {
+ // already fully highlighted, don't re-animate
+ return;
+ }
+
+ if (mAnimatorSet != null) {
+ mAnimatorSet.cancel();
+ }
+ ValueAnimator fpIconAnim;
+ if (isShadeLocked()) {
+ // set color and fade in since we weren't showing before
+ mFingerprintDrawable.setLockScreenColor(mTextColorPrimary);
+ fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 0f, 1f);
+ } else {
+ // update icon color
+ fpIconAnim = new ValueAnimator();
+ fpIconAnim.setIntValues(mWallpaperTexColor, mTextColorPrimary);
+ fpIconAnim.setEvaluator(new ArgbEvaluator());
+ fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor(
+ (Integer) valueAnimator.getAnimatedValue()));
+ }
+
+ mAnimatorSet = new AnimatorSet();
+ mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mAnimatorSet.setDuration(500);
+ mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBgProtection.setVisibility(View.VISIBLE);
+ }
+ });
+
+ mAnimatorSet.playTogether(
+ ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
+ ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
+ ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f),
+ fpIconAnim);
+ mAnimatorSet.start();
+ }
+
+ private boolean isShadeLocked() {
+ return mStatusBarState == StatusBarState.SHADE_LOCKED;
+ }
+
+ /**
+ * Animates out the bg protection circle behind the fp icon to unhighlight the icon.
+ */
+ void animateUnhighlightFp(@Nullable Runnable onEndAnimation) {
+ if (mBgProtection.getVisibility() == View.GONE) {
+ // already hidden
+ return;
+ }
+
+ if (mAnimatorSet != null) {
+ mAnimatorSet.cancel();
+ }
+ ValueAnimator fpIconAnim;
+ if (isShadeLocked()) {
+ // fade out
+ fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 1f, 0f);
+ } else {
+ // update icon color
+ fpIconAnim = new ValueAnimator();
+ fpIconAnim.setIntValues(mTextColorPrimary, mWallpaperTexColor);
+ fpIconAnim.setEvaluator(new ArgbEvaluator());
+ fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor(
+ (Integer) valueAnimator.getAnimatedValue()));
+ }
+
+ mAnimatorSet = new AnimatorSet();
+ mAnimatorSet.playTogether(
+ ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f),
+ ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f),
+ ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f),
+ fpIconAnim);
+ mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mAnimatorSet.setDuration(500);
+
+ mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBgProtection.setVisibility(View.GONE);
+ if (onEndAnimation != null) {
+ onEndAnimation.run();
+ }
+ }
+ });
+ mAnimatorSet.start();
+ }
+
+ boolean isAnimating() {
+ return mAnimatorSet != null && mAnimatorSet.isRunning();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
new file mode 100644
index 000000000000..08e5d562113d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Class that coordinates non-HBM animations during keyguard authentication.
+ */
+public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
+ @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
+
+ private boolean mForceShow;
+
+ protected UdfpsKeyguardViewController(
+ @NonNull UdfpsKeyguardView view,
+ @NonNull StatusBarStateController statusBarStateController,
+ @NonNull StatusBar statusBar,
+ @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ @NonNull DumpManager dumpManager) {
+ super(view, statusBarStateController, statusBar, dumpManager);
+ mKeyguardViewManager = statusBarKeyguardViewManager;
+ }
+
+ @Override
+ @NonNull String getTag() {
+ return "UdfpsKeyguardViewController";
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ final float dozeAmount = mStatusBarStateController.getDozeAmount();
+ mStatusBarStateController.addCallback(mStateListener);
+ mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
+ mStateListener.onStateChanged(mStatusBarStateController.getState());
+ mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mStatusBarStateController.removeCallback(mStateListener);
+ mAlternateAuthInterceptor.reset();
+ mKeyguardViewManager.setAlternateAuthInterceptor(null);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ pw.println("mForceShow=" + mForceShow);
+ }
+
+ /**
+ * Overrides non-force show logic in shouldPauseAuth to still auth.
+ */
+ private void forceShow(boolean forceShow) {
+ if (mForceShow == forceShow) {
+ return;
+ }
+
+ mForceShow = forceShow;
+ updatePauseAuth();
+ if (mForceShow) {
+ mView.animateHighlightFp();
+ } else {
+ mView.animateUnhighlightFp(() -> mKeyguardViewManager.cancelPostAuthActions());
+ }
+ }
+
+ /**
+ * Returns true if the fingerprint manager is running but we want to temporarily pause
+ * authentication. On the keyguard, we may want to show udfps when the shade
+ * is expanded, so this can be overridden with the forceShow method.
+ */
+ public boolean shouldPauseAuth() {
+ if (mForceShow) {
+ return false;
+ }
+ return super.shouldPauseAuth();
+ }
+
+ private final StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ mView.onDozeAmountChanged(linear, eased);
+ if (linear != 0) forceShow(false);
+ }
+
+ @Override
+ public void onStateChanged(int statusBarState) {
+ mView.setStatusBarState(statusBarState);
+ }
+ };
+
+ private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor =
+ new StatusBarKeyguardViewManager.AlternateAuthInterceptor() {
+ @Override
+ public boolean showAlternativeAuthMethod() {
+ if (mForceShow) {
+ return false;
+ }
+
+ forceShow(true);
+ return true;
+ }
+
+ @Override
+ public boolean reset() {
+ if (!mForceShow) {
+ return false;
+ }
+
+ forceShow(false);
+ return true;
+ }
+
+ @Override
+ public boolean isShowingAlternativeAuth() {
+ return mForceShow;
+ }
+
+ @Override
+ public boolean isAnimating() {
+ return mView.isAnimating();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(getTag());
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index a52bddc1dcd5..42d0d8438e15 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -16,10 +16,6 @@
package com.android.systemui.biometrics;
-import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -39,15 +35,12 @@ import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
/**
* A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
* animations.
*/
-public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
- StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
+public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator {
private static final String TAG = "UdfpsView";
private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -56,7 +49,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@NonNull private final Paint mDebugTextPaint;
@NonNull private UdfpsSurfaceView mHbmSurfaceView;
- @Nullable private UdfpsAnimationView mAnimationView;
+ @Nullable private UdfpsAnimationViewController mAnimationViewController;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,8 +57,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
private final float mSensorTouchAreaCoefficient;
@Nullable private String mDebugMessage;
private boolean mIlluminationRequested;
- private int mStatusBarState;
- private boolean mNotificationShadeExpanded;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -108,15 +99,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
mSensorProps = properties;
}
- void setAnimationView(@NonNull UdfpsAnimationView animation) {
- mAnimationView = animation;
- animation.setParent(this);
-
- // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it
- // after the animation type has been decided.
- addView(animation, 0);
- }
-
@Override
public void setHbmCallback(@Nullable HbmCallback callback) {
mHbmSurfaceView.setHbmCallback(callback);
@@ -124,45 +106,38 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void dozeTimeTick() {
- if (mAnimationView == null) {
- return;
- }
- mAnimationView.dozeTimeTick();
- }
-
- @Override
- public void onStateChanged(int newState) {
- mStatusBarState = newState;
- }
-
- @Override
- public void onExpansionChanged(float expansion, boolean expanded) {
- mNotificationShadeExpanded = expanded;
-
- if (mAnimationView != null) {
- mAnimationView.onExpansionChanged(expansion, expanded);
+ if (mAnimationViewController != null) {
+ mAnimationViewController.dozeTimeTick();
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mSensorRect.set(0 + mAnimationView.getPaddingX(),
- 0 + mAnimationView.getPaddingY(),
- 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
- 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+ int paddingX = mAnimationViewController == null ? 0
+ : mAnimationViewController.getPaddingX();
+ int paddingY = mAnimationViewController == null ? 0
+ : mAnimationViewController.getPaddingY();
+ mSensorRect.set(
+ paddingX,
+ paddingY,
+ 2 * mSensorProps.sensorRadius + paddingX,
+ 2 * mSensorProps.sensorRadius + paddingY);
mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
- mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
+ if (mAnimationViewController != null) {
+ mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
+ }
+ }
+
+ void setAnimationViewController(UdfpsAnimationViewController animationViewController) {
+ mAnimationViewController = animationViewController;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Log.v(TAG, "onAttachedToWindow");
-
- // Retrieve the colors each time, since it depends on day/night mode
- mAnimationView.updateColor();
}
@Override
@@ -188,7 +163,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
boolean isWithinSensorArea(float x, float y) {
// The X and Y coordinates of the sensor's center.
- final PointF translation = mAnimationView.getTouchTranslation();
+ final PointF translation = mAnimationViewController == null
+ ? new PointF(0, 0)
+ : mAnimationViewController.getTouchTranslation();
final float cx = mSensorRect.centerX() + translation.x;
final float cy = mSensorRect.centerY() + translation.y;
// Radii along the X and Y axes.
@@ -199,18 +176,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
&& x < (cx + rx * mSensorTouchAreaCoefficient)
&& y > (cy - ry * mSensorTouchAreaCoefficient)
&& y < (cy + ry * mSensorTouchAreaCoefficient)
- && !shouldPauseAuth();
- }
-
- /**
- * States where UDFPS should temporarily not be authenticating. Instead of completely stopping
- * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
- * sending onFingerDown and smoothly animating away.
- */
- boolean shouldPauseAuth() {
- return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
- || mStatusBarState == SHADE_LOCKED
- || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+ && !mAnimationViewController.shouldPauseAuth();
}
boolean isIlluminationRequested() {
@@ -223,7 +189,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
mIlluminationRequested = true;
- mAnimationView.onIlluminationStarting();
+ if (mAnimationViewController != null) {
+ mAnimationViewController.onIlluminationStarting();
+ }
mHbmSurfaceView.setVisibility(View.VISIBLE);
mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
}
@@ -231,7 +199,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void stopIllumination() {
mIlluminationRequested = false;
- mAnimationView.onIlluminationStopped();
+ if (mAnimationViewController != null) {
+ mAnimationViewController.onIlluminationStopped();
+ }
mHbmSurfaceView.setVisibility(View.INVISIBLE);
mHbmSurfaceView.stopIllumination();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 6572ca95b5c3..292af3f069f5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -18,7 +18,6 @@ package com.android.systemui.classifier;
import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS;
-import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS;
import android.net.Uri;
import android.os.Build;
@@ -29,11 +28,11 @@ import androidx.annotation.NonNull;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
-import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.classifier.HistoryTracker.BeliefListener;
import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.FileDescriptor;
@@ -63,14 +62,15 @@ public class BrightLineFalsingManager implements FalsingManager {
private static final int RECENT_INFO_LOG_SIZE = 40;
private static final int RECENT_SWIPE_LOG_SIZE = 20;
+ private static final double TAP_CONFIDENCE_THRESHOLD = 0.7;
+ private static final double FALSE_BELIEF_THRESHOLD = 0.9;
private final FalsingDataProvider mDataProvider;
private final DockManager mDockManager;
private final SingleTapClassifier mSingleTapClassifier;
private final DoubleTapClassifier mDoubleTapClassifier;
private final HistoryTracker mHistoryTracker;
- private final DelayableExecutor mDelayableExecutor;
- private final long mDoubleTapTimeMs;
+ private final KeyguardStateController mKeyguardStateController;
private final boolean mTestHarness;
private final MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
@@ -80,6 +80,7 @@ public class BrightLineFalsingManager implements FalsingManager {
new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1);
private final Collection<FalsingClassifier> mClassifiers;
+ private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
private final SessionListener mSessionListener = new SessionListener() {
@Override
@@ -93,46 +94,37 @@ public class BrightLineFalsingManager implements FalsingManager {
}
};
+ private final BeliefListener mBeliefListener = belief -> {
+ if (belief > FALSE_BELIEF_THRESHOLD) {
+ mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse);
+ }
+ };
+
private final FalsingDataProvider.GestureCompleteListener mGestureCompleteListener =
new FalsingDataProvider.GestureCompleteListener() {
@Override
- public void onGestureComplete(long completionTimeMs) {
- if (mPriorResults != null) {
- // Single taps that may become double taps don't get added right away.
- if (mClassifyAsSingleTap) {
- Collection<FalsingClassifier.Result> singleTapResults = mPriorResults;
- mSingleTapHistoryCanceller = mDelayableExecutor.executeDelayed(
- () -> {
- mSingleTapHistoryCanceller = null;
- mHistoryTracker.addResults(singleTapResults, completionTimeMs);
- },
- mDoubleTapTimeMs);
- mClassifyAsSingleTap = false; // Don't treat things as single taps by default.
- } else {
- mHistoryTracker.addResults(mPriorResults, completionTimeMs);
+ public void onGestureComplete(long completionTimeMs) {
+ if (mPriorResults != null) {
+ mHistoryTracker.addResults(mPriorResults, completionTimeMs);
+ mPriorResults = null;
+ } else {
+ // Gestures that were not classified get treated as a false.
+ mHistoryTracker.addResults(
+ Collections.singleton(
+ FalsingClassifier.Result.falsed(.8, "unclassified")),
+ completionTimeMs);
+ }
}
- mPriorResults = null;
- } else {
- // Gestures that were not classified get treated as a false.
- mHistoryTracker.addResults(
- Collections.singleton(
- FalsingClassifier.Result.falsed(.8, "unclassified")),
- completionTimeMs);
- }
- }
- };
+ };
private Collection<FalsingClassifier.Result> mPriorResults;
- private boolean mClassifyAsSingleTap;
- private Runnable mSingleTapHistoryCanceller;
@Inject
public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
DockManager dockManager, MetricsLogger metricsLogger,
@Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier,
- HistoryTracker historyTracker, @Main DelayableExecutor delayableExecutor,
- @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs,
+ HistoryTracker historyTracker, KeyguardStateController keyguardStateController,
@TestHarness boolean testHarness) {
mDataProvider = falsingDataProvider;
mDockManager = dockManager;
@@ -141,12 +133,12 @@ public class BrightLineFalsingManager implements FalsingManager {
mSingleTapClassifier = singleTapClassifier;
mDoubleTapClassifier = doubleTapClassifier;
mHistoryTracker = historyTracker;
- mDelayableExecutor = delayableExecutor;
- mDoubleTapTimeMs = doubleTapTimeMs;
+ mKeyguardStateController = keyguardStateController;
mTestHarness = testHarness;
mDataProvider.addSessionListener(mSessionListener);
mDataProvider.addGestureCompleteListener(mGestureCompleteListener);
+ mHistoryTracker.addBeliefListener(mBeliefListener);
}
@Override
@@ -156,9 +148,12 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+ if (skipFalsing()) {
+ return false;
+ }
+
boolean result;
- mClassifyAsSingleTap = false;
mDataProvider.setInteractionType(interactionType);
if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
@@ -166,7 +161,7 @@ public class BrightLineFalsingManager implements FalsingManager {
mClassifiers.stream().map(falsingClassifier -> {
FalsingClassifier.Result classifierResult =
falsingClassifier.classifyGesture(
- mHistoryTracker.falsePenalty(),
+ mHistoryTracker.falseBelief(),
mHistoryTracker.falseConfidence());
if (classifierResult.isFalse()) {
logInfo(String.format(
@@ -217,8 +212,10 @@ public class BrightLineFalsingManager implements FalsingManager {
}
@Override
- public boolean isFalseTap(boolean robustCheck) {
- mClassifyAsSingleTap = true;
+ public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+ if (skipFalsing()) {
+ return false;
+ }
FalsingClassifier.Result singleTapResult =
mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
@@ -233,14 +230,24 @@ public class BrightLineFalsingManager implements FalsingManager {
return true;
}
- // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed
if (robustCheck) {
- boolean result = !mDataProvider.isJustUnlockedWithFace();
- mPriorResults = Collections.singleton(
- result ? FalsingClassifier.Result.falsed(0.1, "no face detected")
- : FalsingClassifier.Result.passed(1));
-
- return result;
+ if (mDataProvider.isJustUnlockedWithFace()) {
+ // Immediately pass if a face is detected.
+ mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
+ return false;
+ } else if (!isFalseDoubleTap()) {
+ // We must check double tapping before other heuristics. This is because
+ // the double tap will fail if there's only been one tap. We don't want that
+ // failure to be recorded in mPriorResults.
+ return false;
+ } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) {
+ mPriorResults = Collections.singleton(
+ FalsingClassifier.Result.falsed(0, "bad history"));
+ return true;
+ } else {
+ mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
+ return false;
+ }
}
return false;
@@ -248,7 +255,10 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public boolean isFalseDoubleTap() {
- mClassifyAsSingleTap = false;
+ if (skipFalsing()) {
+ return false;
+ }
+
FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
mPriorResults = Collections.singleton(result);
if (result.isFalse()) {
@@ -258,16 +268,14 @@ public class BrightLineFalsingManager implements FalsingManager {
if (reason != null) {
logInfo(reason);
}
- } else {
- // A valid double tap prevents an invalid single tap from going into history.
- if (mSingleTapHistoryCanceller != null) {
- mSingleTapHistoryCanceller.run();
- mSingleTapHistoryCanceller = null;
- }
}
return result.isFalse();
}
+ private boolean skipFalsing() {
+ return !mKeyguardStateController.isShowing();
+ }
+
@Override
public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
@@ -304,6 +312,16 @@ public class BrightLineFalsingManager implements FalsingManager {
}
@Override
+ public void addFalsingBeliefListener(FalsingBeliefListener listener) {
+ mFalsingBeliefListeners.add(listener);
+ }
+
+ @Override
+ public void removeFalsingBeliefListener(FalsingBeliefListener listener) {
+ mFalsingBeliefListeners.remove(listener);
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println("BRIGHTLINE FALSING MANAGER");
@@ -343,6 +361,8 @@ public class BrightLineFalsingManager implements FalsingManager {
mDataProvider.removeSessionListener(mSessionListener);
mDataProvider.removeGestureCompleteListener(mGestureCompleteListener);
mClassifiers.forEach(FalsingClassifier::cleanup);
+ mFalsingBeliefListeners.clear();
+ mHistoryTracker.removeBeliefListener(mBeliefListener);
}
static void logDebug(String msg) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index bbb937176f59..ffcdb93b11b1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -62,7 +62,7 @@ class DiagonalClassifier extends FalsingClassifier {
VERTICAL_ANGLE_RANGE);
}
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
float angle = getAngle();
if (angle == Float.MAX_VALUE) { // Unknown angle
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 4cb5aa2cce37..0f121c1c9ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -147,7 +147,7 @@ class DistanceClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
return !getPassedFlingThreshold()
? Result.falsed(0.5, getReason()) : Result.passed(0.5);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
index 64576a97ddb2..baa54a65e4fc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
@@ -22,7 +22,6 @@ import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TOUCH_SLO
import android.view.MotionEvent;
import java.util.List;
-import java.util.Queue;
import javax.inject.Inject;
import javax.inject.Named;
@@ -47,15 +46,14 @@ public class DoubleTapClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
List<MotionEvent> secondTapEvents = getRecentMotionEvents();
- Queue<? extends List<MotionEvent>> historicalEvents = getHistoricalEvents();
- List<MotionEvent> firstTapEvents = historicalEvents.peek();
+ List<MotionEvent> firstTapEvents = getPriorMotionEvents();
StringBuilder reason = new StringBuilder();
if (firstTapEvents == null) {
- return Result.falsed(1, "Only one gesture recorded");
+ return Result.falsed(0, "Only one gesture recorded");
}
return !isDoubleTap(firstTapEvents, secondTapEvents, reason)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
index dbfeacfa91c4..1af5f7c488a5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
@@ -21,7 +21,6 @@ import android.view.MotionEvent;
import com.android.systemui.util.sensors.ProximitySensor;
import java.util.List;
-import java.util.Queue;
/**
* Base class for rules that determine False touches.
@@ -40,8 +39,8 @@ public abstract class FalsingClassifier {
return mDataProvider.getRecentMotionEvents();
}
- Queue<? extends List<MotionEvent>> getHistoricalEvents() {
- return mDataProvider.getHistoricalMotionEvents();
+ List<MotionEvent> getPriorMotionEvents() {
+ return mDataProvider.getPriorMotionEvents();
}
MotionEvent getFirstMotionEvent() {
@@ -125,7 +124,7 @@ public abstract class FalsingClassifier {
* See also {@link #classifyGesture(double, double)}.
*/
Result classifyGesture() {
- return calculateFalsingResult(0, 0);
+ return calculateFalsingResult(0.5, 0);
}
/**
@@ -136,16 +135,16 @@ public abstract class FalsingClassifier {
*
* See also {@link #classifyGesture()}.
*/
- Result classifyGesture(double historyPenalty, double historyConfidence) {
- return calculateFalsingResult(historyPenalty, historyConfidence);
+ Result classifyGesture(double historyBelief, double historyConfidence) {
+ return calculateFalsingResult(historyBelief, historyConfidence);
}
/**
* Calculate a result based on available data.
*
- * When passed a historyConfidence of 0, the history penalty should be wholly ignored.
+ * When passed a historyConfidence of 0, the history belief should be wholly ignored.
*/
- abstract Result calculateFalsingResult(double historyPenalty, double historyConfidence);
+ abstract Result calculateFalsingResult(double historyBelief, double historyConfidence);
/** */
public static void logDebug(String msg) {
@@ -165,7 +164,7 @@ public abstract class FalsingClassifier {
/**
* A Falsing result that encapsulates the boolean result along with confidence and a reason.
*/
- static class Result {
+ public static class Result {
private final boolean mFalsed;
private final double mConfidence;
private final String mReason;
@@ -194,14 +193,14 @@ public abstract class FalsingClassifier {
/**
* Construct a "falsed" result indicating that a gesture should be treated as accidental.
*/
- static Result falsed(double confidence, String reason) {
+ public static Result falsed(double confidence, String reason) {
return new Result(true, confidence, reason);
}
/**
* Construct a "passed" result indicating that a gesture should be allowed.
*/
- static Result passed(double confidence) {
+ public static Result passed(double confidence) {
return new Result(false, confidence, null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index b0bbab366e93..bb037202d985 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -120,5 +120,8 @@ public interface FalsingCollector {
/** */
void cleanup();
+
+ /** */
+ void updateFalseConfidence(FalsingClassifier.Result result);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index 12a060439106..939b45a2b4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -153,4 +153,8 @@ public class FalsingCollectorFake implements FalsingCollector {
@Override
public void cleanup() {
}
+
+ @Override
+ public void updateFalseConfidence(FalsingClassifier.Result result) {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index e08b43b3521f..b359860a0fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -27,8 +27,12 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.SystemClock;
+
+import java.util.Collections;
import javax.inject.Inject;
@@ -42,8 +46,11 @@ class FalsingCollectorImpl implements FalsingCollector {
private final FalsingDataProvider mFalsingDataProvider;
private final FalsingManager mFalsingManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final HistoryTracker mHistoryTracker;
private final ProximitySensor mProximitySensor;
private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final SystemClock mSystemClock;
private int mState;
private boolean mShowingAod;
@@ -80,13 +87,17 @@ class FalsingCollectorImpl implements FalsingCollector {
@Inject
FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- ProximitySensor proximitySensor, StatusBarStateController statusBarStateController) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor, HistoryTracker historyTracker,
+ ProximitySensor proximitySensor, StatusBarStateController statusBarStateController,
+ KeyguardStateController keyguardStateController, SystemClock systemClock) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mHistoryTracker = historyTracker;
mProximitySensor = proximitySensor;
mStatusBarStateController = statusBarStateController;
+ mKeyguardStateController = keyguardStateController;
+ mSystemClock = systemClock;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
@@ -247,6 +258,10 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void onTouchEvent(MotionEvent ev) {
+ if (!mKeyguardStateController.isShowing()) {
+ avoidGesture();
+ return;
+ }
// We delay processing down events to see if another component wants to process them.
// If #avoidGesture is called after a MotionEvent.ACTION_DOWN, all following motion events
// will be ignored by the collector until another MotionEvent.ACTION_DOWN is passed in.
@@ -268,8 +283,8 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void avoidGesture() {
+ mAvoidGesture = true;
if (mPendingDownEvent != null) {
- mAvoidGesture = true;
mPendingDownEvent.recycle();
mPendingDownEvent = null;
}
@@ -282,6 +297,11 @@ class FalsingCollectorImpl implements FalsingCollector {
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
+ @Override
+ public void updateFalseConfidence(FalsingClassifier.Result result) {
+ mHistoryTracker.addResults(Collections.singleton(result), mSystemClock.uptimeMillis());
+ }
+
private void updateInteractionType(@Classifier.InteractionType int type) {
logDebug("InteractionType: " + type);
mFalsingDataProvider.setInteractionType(type);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 4bacc1598490..336f13f117d3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -23,13 +23,9 @@ import android.view.MotionEvent.PointerProperties;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.util.time.SystemClock;
import java.util.ArrayList;
-import java.util.Deque;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Queue;
import javax.inject.Inject;
@@ -40,24 +36,23 @@ import javax.inject.Inject;
public class FalsingDataProvider {
private static final long MOTION_EVENT_AGE_MS = 1000;
- private static final long EXTENDED_MOTION_EVENT_AGE_MS = 30 * 1000;
private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
private final int mWidthPixels;
private final int mHeightPixels;
private final BatteryController mBatteryController;
- private final SystemClock mSystemClock;
private final float mXdpi;
private final float mYdpi;
private final List<SessionListener> mSessionListeners = new ArrayList<>();
private final List<MotionEventListener> mMotionEventListeners = new ArrayList<>();
- private final List<GestureCompleteListener> mGestuerCompleteListeners = new ArrayList<>();
+ private final List<GestureCompleteListener> mGestureCompleteListeners = new ArrayList<>();
private @Classifier.InteractionType int mInteractionType;
- private final Deque<TimeLimitedMotionEventBuffer> mExtendedMotionEvents = new LinkedList<>();
private TimeLimitedMotionEventBuffer mRecentMotionEvents =
new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
+ private List<MotionEvent> mPriorMotionEvents;
+
private boolean mDirty = true;
private float mAngle = 0;
@@ -66,14 +61,12 @@ public class FalsingDataProvider {
private boolean mJustUnlockedWithFace;
@Inject
- public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController,
- SystemClock systemClock) {
+ public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) {
mXdpi = displayMetrics.xdpi;
mYdpi = displayMetrics.ydpi;
mWidthPixels = displayMetrics.widthPixels;
mHeightPixels = displayMetrics.heightPixels;
mBatteryController = batteryController;
- mSystemClock = systemClock;
FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
@@ -111,10 +104,10 @@ public class FalsingDataProvider {
private void completePriorGesture() {
if (!mRecentMotionEvents.isEmpty()) {
- mGestuerCompleteListeners.forEach(listener -> listener.onGestureComplete(
+ mGestureCompleteListeners.forEach(listener -> listener.onGestureComplete(
mRecentMotionEvents.get(mRecentMotionEvents.size() - 1).getEventTime()));
- mExtendedMotionEvents.addFirst(mRecentMotionEvents);
+ mPriorMotionEvents = mRecentMotionEvents;
}
}
@@ -140,14 +133,8 @@ public class FalsingDataProvider {
return mRecentMotionEvents;
}
- /** Returns recent gestures, exclusive of the most recent gesture. Newer gestures come first. */
- public Queue<? extends List<MotionEvent>> getHistoricalMotionEvents() {
- long nowMs = mSystemClock.uptimeMillis();
-
- mExtendedMotionEvents.removeIf(
- motionEvents -> motionEvents.isFullyExpired(nowMs - EXTENDED_MOTION_EVENT_AGE_MS));
-
- return mExtendedMotionEvents;
+ public List<MotionEvent> getPriorMotionEvents() {
+ return mPriorMotionEvents;
}
/**
@@ -344,12 +331,12 @@ public class FalsingDataProvider {
/** Register a {@link GestureCompleteListener}. */
public void addGestureCompleteListener(GestureCompleteListener listener) {
- mGestuerCompleteListeners.add(listener);
+ mGestureCompleteListeners.add(listener);
}
/** Unregister a {@link GestureCompleteListener}. */
public void removeGestureCompleteListener(GestureCompleteListener listener) {
- mGestuerCompleteListeners.remove(listener);
+ mGestureCompleteListeners.remove(listener);
}
void onSessionStarted() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index d4d8d06b081b..d39f12488595 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -24,6 +24,8 @@ import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Simple Fake for testing where {@link FalsingManager} is required.
@@ -38,6 +40,8 @@ public class FalsingManagerFake implements FalsingManager {
private boolean mIsReportingEnabled;
private boolean mIsFalseRobustTap;
+ private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
+
@Override
public void onSuccessfulUnlock() {
@@ -76,7 +80,7 @@ public class FalsingManagerFake implements FalsingManager {
}
@Override
- public boolean isFalseTap(boolean robustCheck) {
+ public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
return robustCheck ? mIsFalseRobustTap : mIsFalseTap;
}
@@ -127,4 +131,14 @@ public class FalsingManagerFake implements FalsingManager {
public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
}
+
+ @Override
+ public void addFalsingBeliefListener(FalsingBeliefListener listener) {
+ mFalsingBeliefListeners.add(listener);
+ }
+
+ @Override
+ public void removeFalsingBeliefListener(FalsingBeliefListener listener) {
+ mFalsingBeliefListeners.remove(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index cbec0576e449..9c29f27a2e15 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -131,8 +131,8 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
}
@Override
- public boolean isFalseTap(boolean robustCheck) {
- return mInternalFalsingManager.isFalseTap(robustCheck);
+ public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+ return mInternalFalsingManager.isFalseTap(robustCheck, falsePenalty);
}
@Override
@@ -161,6 +161,16 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
}
@Override
+ public void addFalsingBeliefListener(FalsingBeliefListener listener) {
+ mInternalFalsingManager.addFalsingBeliefListener(listener);
+ }
+
+ @Override
+ public void removeFalsingBeliefListener(FalsingBeliefListener listener) {
+ mInternalFalsingManager.removeFalsingBeliefListener(listener);
+ }
+
+ @Override
public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
mInternalFalsingManager.onProximityEvent(proximityEvent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
index 8bd94a122707..09bf04cb6d59 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
@@ -20,7 +20,9 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.util.time.SystemClock;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@@ -36,15 +38,21 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class HistoryTracker {
- private static final double HISTORY_DECAY = 0.8f;
+ private static final long HISTORY_MAX_AGE_MS = 10000;
+ // A score is decayed discretely every DECAY_INTERVAL_MS.
private static final long DECAY_INTERVAL_MS = 100;
- // We expire items once their decay factor is below 0.001.
- private static final double MINIMUM_SCORE = 0.001;
- private static final long TOTAL_DECAY_TIME_MS =
- DECAY_INTERVAL_MS * (long) (Math.log(MINIMUM_SCORE) / Math.log(HISTORY_DECAY));
+ // We expire items once their decay factor is below 0.1.
+ private static final double MINIMUM_SCORE = 0.1;
+ // This magic number is the factor a score is reduced by every DECAY_INTERVAL_MS.
+ // Once a score is HISTORY_MAX_AGE_MS ms old, it will be reduced by being multiplied by
+ // MINIMUM_SCORE. The math below ensures that.
+ private static final double HISTORY_DECAY =
+ Math.pow(10, Math.log10(MINIMUM_SCORE) / HISTORY_MAX_AGE_MS * DECAY_INTERVAL_MS);
+
private final SystemClock mSystemClock;
DelayQueue<CombinedResult> mResults = new DelayQueue<>();
+ private final List<BeliefListener> mBeliefListeners = new ArrayList<>();
@Inject
HistoryTracker(SystemClock systemClock) {
@@ -54,29 +62,36 @@ public class HistoryTracker {
/**
* Returns how much the HistoryClassifier thinks the past events indicate pocket dialing.
*
- * A result of 0 means that all prior gestures succeeded or there is no data to
- * calculate a score with. Use {@link #falseConfidence()} to differentiate between the
- * two cases.
+ * A result close to 0.5 means that prior data is inconclusive (inconsistent, lacking
+ * confidence, or simply lacking in quantity).
+ *
+ * A result close to 0 means that prior gestures indicate a success.
*
- * A result of 1 means that all prior gestures were very obviously false. The current gesture
- * might be valid, but it should have a high-bar to be classified as such.
+ * A result close to 1 means that prior gestures were very obviously false.
+ *
+ * The current gesture might be different than what is reported by this method, but there should
+ * be a high-bar to be classified differently.
*
* See also {@link #falseConfidence()}.
*/
- double falsePenalty() {
+ double falseBelief() {
//noinspection StatementWithEmptyBody
while (mResults.poll() != null) {
// Empty out the expired results.
}
if (mResults.isEmpty()) {
- return 0;
+ return 0.5;
}
long nowMs = mSystemClock.uptimeMillis();
+ // Get our Bayes on.
return mResults.stream()
.map(result -> result.getDecayedScore(nowMs))
- .reduce(0.0, Double::sum) / mResults.size();
+ .reduce(0.5,
+ (prior, measurement) ->
+ (prior * measurement)
+ / (prior * measurement + (1 - prior) * (1 - measurement)));
}
/**
@@ -91,7 +106,7 @@ public class HistoryTracker {
* A result of 1 means that there are ample, fresh data to act upon that is all consistent
* with each other.
*
- * See als {@link #falsePenalty()}.
+ * See als {@link #falseBelief()}.
*/
double falseConfidence() {
//noinspection StatementWithEmptyBody
@@ -126,14 +141,32 @@ public class HistoryTracker {
finalScore /= results.size();
+ // Never add a 0 or 1, else Bayes breaks down (a 0 and a 1 together results in NaN). In
+ // other words, you shouldn't need Bayes if you have 100% confidence one way or another.
+ // Instead, make the number ever so slightly smaller so that our math never breaks.
+ if (finalScore == 1) {
+ finalScore = 0.99999;
+ } else if (finalScore == 0) {
+ finalScore = 0.00001;
+ }
+
//noinspection StatementWithEmptyBody
while (mResults.poll() != null) {
// Empty out the expired results.
}
mResults.add(new CombinedResult(uptimeMillis, finalScore));
+
+ mBeliefListeners.forEach(beliefListener -> beliefListener.onBeliefChanged(falseBelief()));
+ }
+
+ void addBeliefListener(BeliefListener listener) {
+ mBeliefListeners.add(listener);
}
+ void removeBeliefListener(BeliefListener listener) {
+ mBeliefListeners.remove(listener);
+ }
/**
* Represents a falsing score combing all the classifiers together.
*
@@ -147,15 +180,17 @@ public class HistoryTracker {
private final double mScore;
CombinedResult(long uptimeMillis, double score) {
- mExpiryMs = uptimeMillis + TOTAL_DECAY_TIME_MS;
+ mExpiryMs = uptimeMillis + HISTORY_MAX_AGE_MS;
mScore = score;
}
double getDecayedScore(long nowMs) {
long remainingTimeMs = mExpiryMs - nowMs;
- long decayedTimeMs = TOTAL_DECAY_TIME_MS - remainingTimeMs;
+ long decayedTimeMs = HISTORY_MAX_AGE_MS - remainingTimeMs;
double timeIntervals = (double) decayedTimeMs / DECAY_INTERVAL_MS;
- return mScore * Math.pow(HISTORY_DECAY, timeIntervals);
+
+ // Score should decay towards 0.5.
+ return (mScore - 0.5) * Math.pow(HISTORY_DECAY, timeIntervals) + 0.5;
}
double getScore() {
@@ -174,4 +209,8 @@ public class HistoryTracker {
return Long.compare(ourDelay, otherDelay);
}
}
+
+ interface BeliefListener {
+ void onBeliefChanged(double belief);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
index cd399fe15de1..77d2d4267679 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -56,7 +56,7 @@ class PointerCountClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
int interactionType = getInteractionType();
int allowedPointerCount =
(interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 9ee85986c53c..6e97857f83da 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -112,7 +112,7 @@ class ProximityClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
if (getInteractionType() == QUICK_SETTINGS) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
index f2622ec4e6e9..4dd20ccff98e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
@@ -39,12 +39,15 @@ public class SingleTapClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
return isTap(getRecentMotionEvents());
}
/** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
public Result isTap(List<MotionEvent> motionEvents) {
+ if (motionEvents.isEmpty()) {
+ return Result.falsed(0, "no motion events");
+ }
float downX = motionEvents.get(0).getX();
float downY = motionEvents.get(0).getY();
@@ -59,7 +62,7 @@ public class SingleTapClassifier extends FalsingClassifier {
} else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
reason = "dY too big for a tap: "
+ Math.abs(event.getY() - downY)
- + "vs "
+ + " vs "
+ mTouchSlop;
return Result.falsed(0.5, reason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
index 7969b4e83ac0..e5da38936593 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
@@ -42,18 +42,6 @@ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> {
mMotionEvents = new LinkedList<>();
}
- /**
- * Returns true if the most recent event in the buffer is past the expiration time.
- *
- * This method does not mutate the underlying data. This method does imply that, if the supplied
- * expiration time is old enough and a new {@link MotionEvent} gets added to the buffer, all
- * prior events would be removed.
- */
- public boolean isFullyExpired(long expirationMs) {
- return mMotionEvents.isEmpty()
- || mMotionEvents.getLast().getEventTime() <= expirationMs;
- }
-
private void ejectOldEvents() {
if (mMotionEvents.isEmpty()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index d470d6297170..4e032ea487c9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -38,7 +38,7 @@ public class TypeClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
boolean vertical = isVertical();
boolean up = isUp();
boolean right = isRight();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 2bfb2186191b..205825790461 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -84,7 +84,7 @@ class ZigZagClassifier extends FalsingClassifier {
}
@Override
- Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+ Result calculateFalsingResult(double historyBelief, double historyConfidence) {
List<MotionEvent> motionEvents = getRecentMotionEvents();
// Rotate horizontal gestures to be horizontal between their first and last point.
// Rotate vertical gestures to be vertical between their first and last point.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
new file mode 100644
index 000000000000..3bfdcae2017b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls
+
+import android.service.controls.DeviceTypes.DeviceType
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.controls.ui.ControlViewHolder
+
+/**
+ * Interface for logging UI events related to controls
+ */
+interface ControlsMetricsLogger {
+
+ /**
+ * Assign a new instance id for this controls session, defined as when the controls area is
+ * made visible to when it is closed.
+ */
+ fun assignInstanceId()
+
+ fun touch(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_TOUCH.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ fun drag(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_DRAG.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ fun longPress(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_LONG_PRESS.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ fun refreshBegin(uid: Int, isLocked: Boolean) {
+ assignInstanceId()
+ log(ControlsEvents.CONTROL_REFRESH_BEGIN.id, 0, uid, isLocked)
+ }
+
+ fun refreshEnd(cvh: ControlViewHolder, isLocked: Boolean) {
+ log(ControlsEvents.CONTROL_REFRESH_END.id, cvh.deviceType, cvh.uid, isLocked)
+ }
+
+ /**
+ * Logs a controls-related event
+ *
+ * @param eventId Main UIEvent to capture
+ * @param deviceType One of {@link android.service.controls.DeviceTypes}
+ * @param packageName Package name of the service that receives the request
+ * @param isLocked Is the device locked at the start of the action?
+ */
+ fun log(
+ eventId: Int,
+ @DeviceType deviceType: Int,
+ uid: Int,
+ isLocked: Boolean
+ )
+
+ private enum class ControlsEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "User touched a control")
+ CONTROL_TOUCH(714),
+
+ @UiEvent(doc = "User dragged a control")
+ CONTROL_DRAG(713),
+
+ @UiEvent(doc = "User long-pressed a control")
+ CONTROL_LONG_PRESS(715),
+
+ @UiEvent(doc = "User has opened controls, and a state refresh has begun")
+ CONTROL_REFRESH_BEGIN(716),
+
+ @UiEvent(doc = "User has opened controls, and a state refresh has ended")
+ CONTROL_REFRESH_END(717);
+
+ override fun getId() = metricId
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
new file mode 100644
index 000000000000..c1656327198b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls
+
+import android.service.controls.DeviceTypes.DeviceType
+
+import com.android.internal.logging.InstanceIdSequence
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+
+import javax.inject.Inject
+
+/**
+ * Implementation for logging UI events related to controls
+ */
+@SysUISingleton
+class ControlsMetricsLoggerImpl @Inject constructor() : ControlsMetricsLogger {
+
+ companion object {
+ private const val INSTANCE_ID_MAX = 1 shl 13
+ }
+
+ private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
+ private var instanceId = 0
+
+ override fun assignInstanceId() {
+ instanceId = instanceIdSequence.newInstanceId().id
+ }
+
+ /**
+ * {@see ControlsMetricsLogger#log}
+ */
+ override fun log(
+ eventId: Int,
+ @DeviceType deviceType: Int,
+ uid: Int,
+ isLocked: Boolean
+ ) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.DEVICE_CONTROL_CHANGED,
+ eventId,
+ instanceId,
+ deviceType,
+ uid,
+ isLocked
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index fbdeb30d3911..a165bb2c954f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -18,6 +18,8 @@ package com.android.systemui.controls.dagger
import android.app.Activity
import android.content.pm.PackageManager
+import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.ControlsMetricsLoggerImpl
import com.android.systemui.controls.controller.ControlsBindingController
import com.android.systemui.controls.controller.ControlsBindingControllerImpl
import com.android.systemui.controls.controller.ControlsController
@@ -31,6 +33,7 @@ import com.android.systemui.controls.management.ControlsProviderSelectorActivity
import com.android.systemui.controls.management.ControlsRequestDialog
import com.android.systemui.controls.ui.ControlActionCoordinator
import com.android.systemui.controls.ui.ControlActionCoordinatorImpl
+import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.ControlsUiControllerImpl
import com.android.systemui.dagger.SysUISingleton
@@ -79,6 +82,9 @@ abstract class ControlsModule {
abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
@Binds
+ abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger
+
+ @Binds
abstract fun provideControlActionCoordinator(
coordinator: ControlActionCoordinatorImpl
): ControlActionCoordinator
@@ -113,4 +119,9 @@ abstract class ControlsModule {
abstract fun provideControlsRequestDialog(
activity: ControlsRequestDialog
): Activity
+
+ @Binds
+ @IntoMap
+ @ClassKey(ControlsActivity::class)
+ abstract fun provideControlsActivity(activity: ControlsActivity): Activity
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index fc89783018bc..7dd1d28170b2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -32,7 +32,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker
@@ -112,7 +112,11 @@ class ControlsEditingActivity @Inject constructor(
if (backToGlobalActions) {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
- ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ val i = Intent().apply {
+ component = ComponentName(applicationContext, ControlsActivity::class.java)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ startActivity(i)
}
animateExitAndFinish()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 2d647a907b17..309901443393 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -40,7 +40,7 @@ import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.TooltipManager
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -352,7 +352,11 @@ class ControlsFavoritingActivity @Inject constructor(
if (backToGlobalActions) {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
- ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ val i = Intent().apply {
+ component = ComponentName(applicationContext, ControlsActivity::class.java)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ startActivity(i)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index d5e41d031eac..fa1c41f01e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -32,7 +32,7 @@ import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -116,7 +116,11 @@ class ControlsProviderSelectorActivity @Inject constructor(
if (backToGlobalActions) {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
- ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ val i = Intent().apply {
+ component = ComponentName(applicationContext, ControlsActivity::class.java)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ startActivity(i)
}
animateExitAndFinish()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index d06568a7caf9..0db15e83fc93 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
+import android.content.Context
import android.service.controls.Control
/**
@@ -24,8 +25,8 @@ import android.service.controls.Control
*/
interface ControlActionCoordinator {
- // Handle actions launched from GlobalActionsDialog or ControlDialog
- var startedFromGlobalActions: Boolean
+ // If launched from an Activity, continue within this stack
+ var activityContext: Context?
/**
* Close any dialogs which may have been open
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 6b300f4e07e4..477c22068851 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -32,6 +32,7 @@ import android.util.Log
import android.view.HapticFeedbackConstants
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -53,14 +54,16 @@ class ControlActionCoordinatorImpl @Inject constructor(
private val globalActionsComponent: GlobalActionsComponent,
private val taskViewFactory: Optional<TaskViewFactory>,
private val broadcastDispatcher: BroadcastDispatcher,
- private val lazyUiController: Lazy<ControlsUiController>
+ private val lazyUiController: Lazy<ControlsUiController>,
+ private val controlsMetricsLogger: ControlsMetricsLogger
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
-
- override var startedFromGlobalActions: Boolean = true
+ private val isLocked: Boolean
+ get() = !keyguardStateController.isUnlocked()
+ override var activityContext: Context? = null
companion object {
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
@@ -72,6 +75,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
+ controlsMetricsLogger.touch(cvh, isLocked)
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
cvh.action(BooleanAction(templateId, !isChecked))
@@ -79,11 +83,12 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
+ controlsMetricsLogger.touch(cvh, isLocked)
val blockable = cvh.usePanel()
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
if (cvh.usePanel()) {
- showDialog(cvh, control.getAppIntent().getIntent())
+ showDetail(cvh, control.getAppIntent().getIntent())
} else {
cvh.action(CommandAction(templateId))
}
@@ -99,23 +104,25 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
+ controlsMetricsLogger.drag(cvh, isLocked)
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.action(FloatAction(templateId, newValue))
}, false /* blockable */))
}
override fun longPress(cvh: ControlViewHolder) {
+ controlsMetricsLogger.longPress(cvh, isLocked)
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
// Long press snould only be called when there is valid control state, otherwise ignore
cvh.cws.control?.let {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- showDialog(cvh, it.getAppIntent().getIntent())
+ showDetail(cvh, it.getAppIntent().getIntent())
}
}, false /* blockable */))
}
override fun runPendingAction(controlId: String) {
- if (!keyguardStateController.isUnlocked()) return
+ if (isLocked) return
if (pendingAction?.controlId == controlId) {
pendingAction?.invoke()
pendingAction = null
@@ -140,22 +147,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
@VisibleForTesting
fun bouncerOrRun(action: Action) {
if (keyguardStateController.isShowing()) {
- var closeDialog = !keyguardStateController.isUnlocked()
- if (closeDialog) {
+ if (isLocked) {
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
// pending actions will only run after the control state has been refreshed
pendingAction = action
}
-
+ val wasLocked = isLocked
activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
- if (closeDialog) {
- if (startedFromGlobalActions) {
- globalActionsComponent.handleShowGlobalActionsMenu()
- } else {
- ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get())
- }
+ if (wasLocked && activityContext == null) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
} else {
action.invoke()
}
@@ -170,9 +172,9 @@ class ControlActionCoordinatorImpl @Inject constructor(
bgExecutor.execute { vibrator.vibrate(effect) }
}
- private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
+ private fun showDetail(cvh: ControlViewHolder, intent: Intent) {
bgExecutor.execute {
- val activities: List<ResolveInfo> = cvh.context.packageManager.queryIntentActivities(
+ val activities: List<ResolveInfo> = context.packageManager.queryIntentActivities(
intent,
PackageManager.MATCH_DEFAULT_ONLY
)
@@ -180,8 +182,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
uiExecutor.execute {
// make sure the intent is valid before attempting to open the dialog
if (activities.isNotEmpty() && taskViewFactory.isPresent) {
- taskViewFactory.get().create(cvh.context, uiExecutor, {
- dialog = DetailDialog(cvh, it, intent).also {
+ taskViewFactory.get().create(context, uiExecutor, {
+ dialog = DetailDialog(activityContext, it, intent, cvh).also {
it.setOnDismissListener { _ -> dialog = null }
it.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 9d92a40beec4..3e0289022162 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -49,6 +49,7 @@ import android.widget.TextView
import com.android.internal.graphics.ColorUtils
import com.android.systemui.Interpolators
import com.android.systemui.R
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.util.concurrency.DelayableExecutor
import kotlin.reflect.KClass
@@ -63,7 +64,9 @@ class ControlViewHolder(
val controlsController: ControlsController,
val uiExecutor: DelayableExecutor,
val bgExecutor: DelayableExecutor,
- val controlActionCoordinator: ControlActionCoordinator
+ val controlActionCoordinator: ControlActionCoordinator,
+ val controlsMetricsLogger: ControlsMetricsLogger,
+ val uid: Int
) {
companion object {
@@ -141,7 +144,7 @@ class ControlViewHolder(
status.setSelected(true)
}
- fun bindData(cws: ControlWithState) {
+ fun bindData(cws: ControlWithState, isLocked: Boolean) {
// If an interaction is in progress, the update may visually interfere with the action the
// action the user wants to make. Don't apply the update, and instead assume a new update
// will coming from when the user interaction is complete.
@@ -171,10 +174,16 @@ class ControlViewHolder(
controlActionCoordinator.runPendingAction(cws.ci.controlId)
}
+ val wasLoading = isLoading
isLoading = false
behavior = bindBehavior(behavior,
findBehaviorClass(controlStatus, controlTemplate, deviceType))
updateContentDescription()
+
+ // Only log one event per control, at the moment we have determined that the control
+ // switched from the loading to done state
+ val doneLoading = wasLoading && !isLoading
+ if (doneLoading) controlsMetricsLogger.refreshEnd(this, isLocked)
}
fun actionResponse(@ControlAction.ResponseResult response: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
new file mode 100644
index 000000000000..a35b792ac7f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowInsets.Type
+
+import com.android.systemui.R
+import com.android.systemui.controls.management.ControlsAnimations
+import com.android.systemui.util.LifecycleActivity
+import javax.inject.Inject
+
+/**
+ * Displays Device Controls inside an activity
+ */
+class ControlsActivity @Inject constructor(
+ private val uiController: ControlsUiController
+) : LifecycleActivity() {
+
+ private lateinit var parent: ViewGroup
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.controls_fullscreen)
+
+ requireViewById<ViewGroup>(R.id.control_detail_root).apply {
+ setOnApplyWindowInsetsListener {
+ v: View, insets: WindowInsets ->
+ v.apply {
+ val l = getPaddingLeft()
+ val t = getPaddingTop()
+ val r = getPaddingRight()
+ setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
+ }
+
+ WindowInsets.CONSUMED
+ }
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
+ parent.alpha = 0f
+ uiController.show(parent, { animateExitAndFinish() }, this)
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ ControlsAnimations.enterAnimation(parent).start()
+ }
+
+ override fun onBackPressed() {
+ animateExitAndFinish()
+ }
+
+ override fun onStop() {
+ super.onStop()
+
+ uiController.hide()
+ }
+
+ private fun animateExitAndFinish() {
+ ControlsAnimations.exitAnimation(parent, { finish() }).start()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
deleted file mode 100644
index 537334aeb2f7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.controls.ui
-
-import android.app.Dialog
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowManager
-
-import com.android.systemui.Interpolators
-import com.android.systemui.R
-import com.android.systemui.broadcast.BroadcastDispatcher
-import javax.inject.Inject
-
-/**
- * Show the controls space inside a dialog, as from the lock screen.
- */
-class ControlsDialog @Inject constructor(
- thisContext: Context,
- val broadcastDispatcher: BroadcastDispatcher
-) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
-
- private val receiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- val action = intent.getAction()
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- dismiss()
- }
- }
- }
-
- init {
- window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
- window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
-
- setContentView(R.layout.controls_in_dialog)
-
- requireViewById<ViewGroup>(R.id.control_detail_root).apply {
- setOnClickListener { dismiss() }
- (getParent() as View).setOnClickListener { dismiss() }
- }
- }
-
- fun show(
- controller: ControlsUiController
- ): ControlsDialog {
- super.show()
-
- val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
- vg.alpha = 0f
- controller.show(vg, { dismiss() }, false /* startedFromGlobalActions */)
-
- vg.animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setDuration(300)
-
- val filter = IntentFilter()
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
- broadcastDispatcher.registerReceiver(receiver, filter)
-
- return this
- }
-
- override fun dismiss() {
- broadcastDispatcher.unregisterReceiver(receiver)
-
- if (!isShowing()) return
-
- super.dismiss()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 20bdf609357e..f86948ee8957 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.ui
import android.content.ComponentName
+import android.content.Context
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.view.ViewGroup
@@ -30,7 +31,7 @@ interface ControlsUiController {
public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
}
- fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
+ fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context?)
fun hide()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index c94d85aa58c4..d08882b1dbd2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -43,6 +43,7 @@ import android.widget.ListPopupWindow
import android.widget.Space
import android.widget.TextView
import com.android.systemui.R
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlInfo
@@ -58,6 +59,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsPopupMenu
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Lazy
import java.text.Collator
@@ -77,7 +79,9 @@ class ControlsUiControllerImpl @Inject constructor (
val controlActionCoordinator: ControlActionCoordinator,
private val activityStarter: ActivityStarter,
private val shadeController: ShadeController,
- private val iconCache: CustomIconCache
+ private val iconCache: CustomIconCache,
+ private val controlsMetricsLogger: ControlsMetricsLogger,
+ private val keyguardStateController: KeyguardStateController
) : ControlsUiController {
companion object {
@@ -133,7 +137,8 @@ class ControlsUiControllerImpl @Inject constructor (
return object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
val lastItems = serviceInfos.map {
- SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
+ val uid = it.serviceInfo.applicationInfo.uid
+ SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName, uid)
}
uiExecutor.execute {
parent.removeAllViews()
@@ -148,7 +153,7 @@ class ControlsUiControllerImpl @Inject constructor (
override fun show(
parent: ViewGroup,
onDismiss: Runnable,
- startedFromGlobalActions: Boolean
+ activityContext: Context?
) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
@@ -156,7 +161,7 @@ class ControlsUiControllerImpl @Inject constructor (
hidden = false
retainCache = false
- controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions
+ controlActionCoordinator.activityContext = activityContext
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
@@ -193,7 +198,7 @@ class ControlsUiControllerImpl @Inject constructor (
controlViewsById.clear()
controlsById.clear()
- show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions)
+ show(parent, onDismiss, controlActionCoordinator.activityContext)
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
showAnim.setDuration(FADE_IN_MILLIS)
@@ -268,7 +273,7 @@ class ControlsUiControllerImpl @Inject constructor (
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
intent.putExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
- controlActionCoordinator.startedFromGlobalActions
+ controlActionCoordinator.activityContext == null
)
onDismiss.run()
@@ -282,8 +287,19 @@ class ControlsUiControllerImpl @Inject constructor (
private fun showControlsView(items: List<SelectionItem>) {
controlViewsById.clear()
- createListView()
- createDropDown(items)
+ val itemsByComponent = items.associateBy { it.componentName }
+ val itemsWithStructure = mutableListOf<SelectionItem>()
+ allStructures.mapNotNullTo(itemsWithStructure) {
+ itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
+ }
+ itemsWithStructure.sortWith(localeComparator)
+
+ val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
+
+ controlsMetricsLogger.refreshBegin(selectionItem.uid, !keyguardStateController.isUnlocked())
+
+ createListView(selectionItem)
+ createDropDown(itemsWithStructure, selectionItem)
createMenu()
}
@@ -325,22 +341,13 @@ class ControlsUiControllerImpl @Inject constructor (
})
}
- private fun createDropDown(items: List<SelectionItem>) {
+ private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) {
items.forEach {
RenderInfo.registerComponentIcon(it.componentName, it.icon)
}
- val itemsByComponent = items.associateBy { it.componentName }
- val itemsWithStructure = mutableListOf<SelectionItem>()
- allStructures.mapNotNullTo(itemsWithStructure) {
- itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
- }
- itemsWithStructure.sortWith(localeComparator)
-
- val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
-
var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply {
- addAll(itemsWithStructure)
+ addAll(items)
}
/*
@@ -349,13 +356,13 @@ class ControlsUiControllerImpl @Inject constructor (
* a similar effect
*/
val spinner = parent.requireViewById<TextView>(R.id.app_or_structure_spinner).apply {
- setText(selectionItem.getTitle())
+ setText(selected.getTitle())
// override the default color on the dropdown drawable
(getBackground() as LayerDrawable).getDrawable(0)
.setTint(context.resources.getColor(R.color.control_spinner_dropdown, null))
}
- if (itemsWithStructure.size == 1) {
+ if (items.size == 1) {
spinner.setBackground(null)
return
}
@@ -388,10 +395,21 @@ class ControlsUiControllerImpl @Inject constructor (
})
}
- private fun createListView() {
+ private fun createListView(selected: SelectionItem) {
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_with_favorites, parent, true)
+ if (controlActionCoordinator.activityContext == null) {
+ parent.requireViewById<View>(R.id.controls_spacer).apply {
+ visibility = View.VISIBLE
+ }
+ } else {
+ parent.requireViewById<ImageView>(R.id.controls_close).apply {
+ setOnClickListener { _: View -> onDismiss.run() }
+ visibility = View.VISIBLE
+ }
+ }
+
val maxColumns = findMaxColumns()
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
@@ -410,9 +428,11 @@ class ControlsUiControllerImpl @Inject constructor (
controlsController.get(),
uiExecutor,
bgExecutor,
- controlActionCoordinator
+ controlActionCoordinator,
+ controlsMetricsLogger,
+ selected.uid
)
- cvh.bindData(it)
+ cvh.bindData(it, false /* isLocked, will be ignored on initial load */)
controlViewsById.put(key, cvh)
}
}
@@ -502,6 +522,8 @@ class ControlsUiControllerImpl @Inject constructor (
override fun hide() {
hidden = true
+ controlActionCoordinator.activityContext = null
+
closeDialogs(true)
controlsController.get().unsubscribe()
@@ -515,6 +537,7 @@ class ControlsUiControllerImpl @Inject constructor (
}
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
+ val isLocked = !keyguardStateController.isUnlocked()
controls.forEach { c ->
controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
@@ -523,8 +546,8 @@ class ControlsUiControllerImpl @Inject constructor (
val key = ControlKey(componentName, c.getControlId())
controlsById.put(key, cws)
- uiExecutor.execute {
- controlViewsById.get(key)?.bindData(cws)
+ controlViewsById.get(key)?.let {
+ uiExecutor.execute { it.bindData(cws, isLocked) }
}
}
}
@@ -553,7 +576,8 @@ private data class SelectionItem(
val appName: CharSequence,
val structure: CharSequence,
val icon: Drawable,
- val componentName: ComponentName
+ val componentName: ComponentName,
+ val uid: Int
) {
fun getTitle() = if (structure.isEmpty()) { appName } else { structure }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 020694d89fce..9c788df362de 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -22,8 +22,8 @@ import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.Dialog
import android.app.PendingIntent
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
-import android.provider.Settings
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
@@ -35,18 +35,20 @@ import com.android.systemui.R
import com.android.wm.shell.TaskView
/**
- * A dialog that provides an {@link ActivityView}, allowing the application to provide
+ * A dialog that provides an {@link TaskView}, allowing the application to provide
* additional information and actions pertaining to a {@link android.service.controls.Control}.
* The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
*/
class DetailDialog(
- val cvh: ControlViewHolder,
- val activityView: TaskView,
- val intent: Intent
-) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) {
-
+ val activityContext: Context?,
+ val taskView: TaskView,
+ val intent: Intent,
+ val cvh: ControlViewHolder
+) : Dialog(
+ activityContext ?: cvh.context,
+ R.style.Theme_SystemUI_Dialog_Control_DetailPanel
+) {
companion object {
- private const val PANEL_TOP_OFFSET = "systemui.controls_panel_top_offset"
/*
* Indicate to the activity that it is being rendered in a bottomsheet, and they
* should optimize the layout for a smaller space.
@@ -71,10 +73,19 @@ class DetailDialog(
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
- activityView.startActivity(
- PendingIntent.getActivity(context, 0, launchIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
- null, ActivityOptions.makeBasic())
+ val options = activityContext?.let {
+ ActivityOptions.makeCustomAnimation(
+ it,
+ 0 /* enterResId */,
+ 0 /* exitResId */
+ )
+ } ?: ActivityOptions.makeBasic()
+ taskView.startActivity(
+ PendingIntent.getActivity(context, 0, launchIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
+ null,
+ options
+ )
}
override fun onTaskRemovalStarted(taskId: Int) {
@@ -92,7 +103,10 @@ class DetailDialog(
}
init {
- window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ if (activityContext == null) {
+ window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ }
+
// To pass touches to the task inside TaskView.
window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
@@ -100,7 +114,7 @@ class DetailDialog(
setContentView(R.layout.controls_detail_dialog)
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
- addView(activityView)
+ addView(taskView)
}
requireViewById<ImageView>(R.id.control_detail_close).apply {
@@ -120,48 +134,34 @@ class DetailDialog(
// consume all insets to achieve slide under effect
window.getDecorView().setOnApplyWindowInsetsListener {
- _: View, insets: WindowInsets ->
- activityView.apply {
+ v: View, insets: WindowInsets ->
+ taskView.apply {
val l = getPaddingLeft()
val t = getPaddingTop()
val r = getPaddingRight()
setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
}
- WindowInsets.CONSUMED
- }
-
- requireViewById<ViewGroup>(R.id.control_detail_root).apply {
- // use flag only temporarily for testing
- val resolver = cvh.context.contentResolver
- val defaultOffsetInPx = cvh.context.resources
- .getDimensionPixelSize(R.dimen.controls_activity_view_top_offset)
- val offsetInPx = Settings.Secure.getInt(resolver, PANEL_TOP_OFFSET, defaultOffsetInPx)
-
- val lp = getLayoutParams() as ViewGroup.MarginLayoutParams
- lp.topMargin = offsetInPx
- setLayoutParams(lp)
+ val l = v.getPaddingLeft()
+ val b = v.getPaddingBottom()
+ val r = v.getPaddingRight()
+ v.setPadding(l, insets.getInsets(Type.systemBars()).top, r, b)
- setOnClickListener { dismiss() }
- (getParent() as View).setOnClickListener { dismiss() }
+ WindowInsets.CONSUMED
}
if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(context.getResources())) {
val cornerRadius = context.resources
.getDimensionPixelSize(R.dimen.controls_activity_view_corner_radius)
- activityView.setCornerRadius(cornerRadius.toFloat())
+ taskView.setCornerRadius(cornerRadius.toFloat())
}
- }
-
- override fun show() {
- activityView.setListener(cvh.uiExecutor, stateCallback)
- super.show()
+ taskView.setListener(cvh.uiExecutor, stateCallback)
}
override fun dismiss() {
if (!isShowing()) return
- activityView.release()
+ taskView.release()
super.dismiss()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index a2f96bbad203..8e344d2c2df7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -31,6 +31,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -324,6 +325,7 @@ public class DependencyProvider {
/** */
@Provides
+ @SysUISingleton
public AlwaysOnDisplayPolicy provideAlwaysOnDisplayPolicy(Context context) {
return new AlwaysOnDisplayPolicy(context);
}
@@ -349,21 +351,21 @@ public class DependencyProvider {
/** */
@Provides
@SysUISingleton
- public SystemActions providesSystemActions(Context context) {
- return new SystemActions(context);
+ public Choreographer providesChoreographer() {
+ return Choreographer.getInstance();
}
/** */
@Provides
@SysUISingleton
- public Choreographer providesChoreographer() {
- return Choreographer.getInstance();
+ public ModeSwitchesController providesModeSwitchesController(Context context) {
+ return new ModeSwitchesController(context);
}
/** */
@Provides
@SysUISingleton
- public ModeSwitchesController providesModeSwitchesController(Context context) {
- return new ModeSwitchesController(context);
+ public QuickAccessWalletClient provideQuickAccessWalletClient(Context context) {
+ return QuickAccessWalletClient.create(context);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index c26cc4466c6d..d8ade2bdd21f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -225,8 +225,8 @@ public class DozeLog implements Dumpable {
* Appends wake-display event to the logs.
* @param wake if we're waking up or sleeping.
*/
- public void traceWakeDisplay(boolean wake) {
- mLogger.logWakeDisplay(wake);
+ public void traceWakeDisplay(boolean wake, @Reason int reason) {
+ mLogger.logWakeDisplay(wake, reason);
}
/**
@@ -380,6 +380,7 @@ public class DozeLog implements Dumpable {
case REASON_SENSOR_WAKE_UP: return "wakeup";
case REASON_SENSOR_TAP: return "tap";
case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps";
+ case REASON_SENSOR_QUICK_PICKUP: return "quickPickup";
default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
}
}
@@ -389,7 +390,7 @@ public class DozeLog implements Dumpable {
PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP,
- REASON_SENSOR_UDFPS_LONG_PRESS})
+ REASON_SENSOR_UDFPS_LONG_PRESS, REASON_SENSOR_QUICK_PICKUP})
public @interface Reason {}
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -403,6 +404,7 @@ public class DozeLog implements Dumpable {
public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
public static final int REASON_SENSOR_TAP = 9;
public static final int REASON_SENSOR_UDFPS_LONG_PRESS = 10;
+ public static final int REASON_SENSOR_QUICK_PICKUP = 11;
- public static final int TOTAL_REASONS = 11;
+ public static final int TOTAL_REASONS = 12;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index fddefae6f5b6..9bc74be9b9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -16,6 +16,7 @@
package com.android.systemui.doze
+import android.view.Display
import com.android.systemui.doze.DozeLog.Reason
import com.android.systemui.doze.DozeLog.reasonToString
import com.android.systemui.log.LogBuffer
@@ -161,17 +162,18 @@ class DozeLogger @Inject constructor(
fun logDisplayStateChanged(displayState: Int) {
buffer.log(TAG, INFO, {
- int1 = displayState
+ str1 = Display.stateToString(displayState)
}, {
- "Display state changed to $int1"
+ "Display state changed to $str1"
})
}
- fun logWakeDisplay(isAwake: Boolean) {
+ fun logWakeDisplay(isAwake: Boolean, @Reason reason: Int) {
buffer.log(TAG, DEBUG, {
bool1 = isAwake
+ int1 = reason
}, {
- "Display wakefulness changed, isAwake=$bool1"
+ "Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index c542e5b07d9b..52c9f164a16e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -113,6 +113,8 @@ public class DozeSensors {
mCallback = callback;
mProximitySensor = proximitySensor;
+ boolean udfpsEnrolled =
+ authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
new TriggerSensor(
@@ -159,7 +161,7 @@ public class DozeSensors {
findSensorWithType(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
true /* settingDef */,
- authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser()),
+ udfpsEnrolled,
DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
@@ -181,6 +183,15 @@ public class DozeSensors {
false /* touchscreen */,
mConfig.getWakeLockScreenDebounce(),
dozeLog),
+ new TriggerSensor(
+ findSensorWithType(config.quickPickupSensorType()),
+ Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
+ false /* setting default */,
+ config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
+ && udfpsEnrolled,
+ DozeLog.REASON_SENSOR_QUICK_PICKUP,
+ false /* touchCoords */,
+ false /* touchscreen */, dozeLog),
};
setProxListening(false); // Don't immediately start listening when we register.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index c617f3d751d0..04b46705226f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -42,10 +42,13 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
@@ -76,6 +79,7 @@ public class DozeTriggers implements DozeMachine.Part {
* Assuming that the screen should start on.
*/
private static boolean sWakeDisplaySensorState = true;
+ private Runnable mQuickPickupDozeCancellable;
private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
@@ -96,6 +100,8 @@ public class DozeTriggers implements DozeMachine.Part {
private final ProximitySensor.ProximityCheck mProxCheck;
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
+ private final DelayableExecutor mMainExecutor;
+ private final DelayableExecutor mBgExecutor;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -135,7 +141,10 @@ public class DozeTriggers implements DozeMachine.Part {
DOZING_UPDATE_SENSOR_TAP(441),
@UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.")
- DOZING_UPDATE_AUTH_TRIGGERED(657);
+ DOZING_UPDATE_AUTH_TRIGGERED(657),
+
+ @UiEvent(doc = "Dozing updated because quick pickup sensor woke up.")
+ DOZING_UPDATE_QUICK_PICKUP(708);
private final int mId;
@@ -160,6 +169,7 @@ public class DozeTriggers implements DozeMachine.Part {
case 8: return DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN;
case 9: return DOZING_UPDATE_SENSOR_TAP;
case 10: return DOZING_UPDATE_AUTH_TRIGGERED;
+ case 11: return DOZING_UPDATE_QUICK_PICKUP;
default: return null;
}
}
@@ -172,7 +182,8 @@ public class DozeTriggers implements DozeMachine.Part {
WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
- SecureSettings secureSettings, AuthController authController) {
+ SecureSettings secureSettings, AuthController authController,
+ @Main DelayableExecutor mainExecutor, @Background DelayableExecutor bgExecutor) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -189,6 +200,8 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeLog = dozeLog;
mBroadcastDispatcher = broadcastDispatcher;
mAuthController = authController;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
}
@Override
@@ -262,18 +275,22 @@ public class DozeTriggers implements DozeMachine.Part {
boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
- boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
- boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isWakeOnPresence = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+ boolean isWakeOnReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
boolean isUdfpsLongPress = pulseReason == DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
- boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
-
- if (isWakeDisplay) {
- onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
+ boolean isQuickPickup = pulseReason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
+ boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach)
+ && rawValues != null && rawValues.length > 0 && rawValues[0] != 0);
+
+ if (isWakeOnPresence || isQuickPickup) {
+ onWakeScreen(isQuickPickup || isWakeDisplayEvent,
+ mMachine.isExecutingTransition() ? null : mMachine.getState(),
+ pulseReason);
} else if (isLongPress) {
requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
null /* onPulseSuppressedListener */);
- } else if (isWakeLockScreen) {
- if (wakeEvent) {
+ } else if (isWakeOnReach) {
+ if (isWakeDisplayEvent) {
requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
null /* onPulseSuppressedListener */);
}
@@ -370,13 +387,17 @@ public class DozeTriggers implements DozeMachine.Part {
* @param state The current state, or null if the state could not be determined due to enqueued
* transitions.
*/
- private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) {
- mDozeLog.traceWakeDisplay(wake);
- sWakeDisplaySensorState = wake;
+ private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) {
+ mDozeLog.traceWakeDisplay(wake, reason);
+ final boolean isWakeOnPresence = reason == DozeLog.REASON_SENSOR_WAKE_UP;
+ final boolean isQuickPickup = reason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
+ if (isWakeOnPresence) {
+ sWakeDisplaySensorState = wake;
+ }
if (wake) {
proximityCheckThenCall((result) -> {
- if (result != null && result) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -385,26 +406,51 @@ public class DozeTriggers implements DozeMachine.Part {
// Logs AOD open due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_OPEN)
- .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+ .setSubtype(reason));
+
+ if (isQuickPickup) {
+ // schedule runnable to go back to DOZE
+ onQuickPickup();
+ }
+ } else if (state == DozeMachine.State.DOZE_AOD && isQuickPickup) {
+ // elongate time in DOZE_AOD, schedule new runnable to go back to DOZE
+ onQuickPickup();
}
- }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+ }, isQuickPickup /* alreadyPerformedProxCheck */, reason);
} else {
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+ boolean pulse = (state == DozeMachine.State.DOZE_REQUEST_PULSE)
+ || (state == DozeMachine.State.DOZE_PULSING)
+ || (state == DozeMachine.State.DOZE_PULSING_BRIGHT);
+ boolean docked = (state == DozeMachine.State.DOZE_AOD_DOCKED);
if (!pausing && !paused) {
+ if (isQuickPickup && (pulse || docked)) {
+ return;
+ }
mMachine.requestState(DozeMachine.State.DOZE);
// Logs AOD close due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_CLOSE)
- .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+ .setSubtype(reason));
}
}
}
+ private void onQuickPickup() {
+ cancelQuickPickupDelayableDoze();
+ mQuickPickupDozeCancellable = mMainExecutor.executeDelayed(() -> {
+ onWakeScreen(false,
+ mMachine.isExecutingTransition() ? null : mMachine.getState(),
+ DozeLog.REASON_SENSOR_QUICK_PICKUP);
+ }, mDozeParameters.getQuickPickupAodDuration());
+ }
+
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
+ sWakeDisplaySensorState = true;
mBroadcastReceiver.register(mBroadcastDispatcher);
mDozeHost.addCallback(mHostCallback);
mDockManager.addListener(mDockEventListener);
@@ -417,7 +463,7 @@ public class DozeTriggers implements DozeMachine.Part {
mWantSensors = true;
mWantTouchScreenSensors = true;
if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
- onWakeScreen(false, newState);
+ onWakeScreen(false, newState, DozeLog.REASON_SENSOR_WAKE_UP);
}
break;
case DOZE_AOD_PAUSED:
@@ -437,6 +483,7 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors.requestTemporaryDisable();
break;
case FINISH:
+ cancelQuickPickupDelayableDoze();
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
@@ -460,6 +507,17 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
+ /**
+ * Cancels last scheduled Runnable that transitions to STATE_DOZE (blank screen) after
+ * going into STATE_AOD (AOD screen) from the quick pickup gesture.
+ */
+ private void cancelQuickPickupDelayableDoze() {
+ if (mQuickPickupDozeCancellable != null) {
+ mQuickPickupDozeCancellable.run();
+ mQuickPickupDozeCancellable = null;
+ }
+ }
+
private void checkTriggersAtInit() {
if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
|| mDozeHost.isBlockingDoze()
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d85b10167697..461a7303c184 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -79,7 +79,6 @@ import android.transition.AutoTransition;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.ArraySet;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.IWindowManager;
@@ -114,7 +113,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.EmergencyAffordanceManager;
-import com.android.internal.util.ScreenRecordHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.LockPatternUtils;
@@ -242,7 +240,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
private final ScreenshotHelper mScreenshotHelper;
- private final ScreenRecordHelper mScreenRecordHelper;
private final ActivityStarter mActivityStarter;
private final SysuiColorExtractor mSysuiColorExtractor;
private final IStatusBarService mStatusBarService;
@@ -378,7 +375,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
- mScreenRecordHelper = new ScreenRecordHelper(context);
mConfigurationController.addCallback(this);
@@ -979,7 +975,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
@VisibleForTesting
- class ScreenshotAction extends SinglePressAction implements LongPressAction {
+ class ScreenshotAction extends SinglePressAction {
final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons";
public ScreenshotAction() {
@@ -1024,18 +1020,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
}
-
-
- @Override
- public boolean onLongPress() {
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
- mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
- mScreenRecordHelper.launchRecordPrompt();
- } else {
- onPress();
- }
- return true;
- }
}
@VisibleForTesting
@@ -2236,7 +2220,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private void showControls(ControlsUiController controller) {
mControlsUiController = controller;
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
- true /* startedFromGlobalActions */);
+ null /* activityContext */);
}
private boolean isWalletViewAvailable() {
@@ -2465,7 +2449,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
});
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
- true /* startedFromGlobalActions */);
+ null /* activityContext */);
}
mBackgroundDrawable.setAlpha(0);
@@ -2641,7 +2625,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mGlobalActionsLayout.updateList();
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
- true /* startedFromGlobalActions */);
+ null /* activityContext */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 17f7ccf0d967..97803c1cf2fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -149,9 +149,14 @@ public class KeyguardService extends Service {
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
- // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
- // run animation.
try {
+ if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
+ mBinder.setOccluded(true /* isOccluded */, true /* animate */);
+ } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
+ mBinder.setOccluded(false /* isOccluded */, true /* animate */);
+ }
+ // TODO(bc-unlock): Implement occlude/unocclude animation applied on apps,
+ // wallpapers and nonApps.
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 55c55b97db51..41c9daedf2d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -476,7 +476,6 @@ class MediaDataManager(
Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf<Int>()
// TODO: b/153736623 look into creating actions when this isn't a media style notification
- val packageContext: Context = sbn.getPackageContext(context)
if (actions != null) {
for ((index, action) in actions.withIndex()) {
if (action.getIcon() == null) {
@@ -499,7 +498,7 @@ class MediaDataManager(
null
}
val mediaActionIcon = if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) {
- Icon.createWithResource(packageContext, action.getIcon()!!.getResId())
+ Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
} else {
action.getIcon()
}.setTint(themeText)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 553623702aed..9d298221fb79 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -35,6 +35,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -85,6 +86,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
@@ -211,7 +213,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private Locale mLocale;
private int mLayoutDirection;
+ private boolean mAllowForceNavBarHandleOpaque;
private boolean mForceNavBarHandleOpaque;
+ private Optional<Long> mHomeButtonLongPressDurationMs;
private boolean mIsCurrentUserSetup;
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
@@ -333,13 +337,23 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
// If the current user is not yet setup, then don't update any button alphas
return;
}
+ if (QuickStepContract.isLegacyMode(mNavBarMode)) {
+ // Don't allow the bar buttons to be affected by the alpha
+ return;
+ }
+
ButtonDispatcher buttonDispatcher = null;
boolean forceVisible = false;
- if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
- buttonDispatcher = mNavigationBarView.getBackButton();
- } else if (QuickStepContract.isGesturalMode(mNavBarMode)) {
- forceVisible = mForceNavBarHandleOpaque;
+ if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+ // Disallow home handle animations when in gestural
+ animate = false;
+ forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
buttonDispatcher = mNavigationBarView.getHomeHandle();
+ if (getBarTransitions() != null) {
+ getBarTransitions().setBackgroundOverrideAlpha(alpha);
+ }
+ } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
+ buttonDispatcher = mNavigationBarView.getBackButton();
}
if (buttonDispatcher != null) {
buttonDispatcher.setVisibility(
@@ -373,6 +387,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
+ private final Runnable mOnVariableDurationHomeLongClick = () -> {
+ if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) {
+ mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+ };
+
private final ContentObserver mAssistContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
@@ -394,6 +416,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mForceNavBarHandleOpaque = properties.getBoolean(
NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
}
+
+ if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
+ mHomeButtonLongPressDurationMs = Optional.of(
+ properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
+ ).filter(duration -> duration != 0);
+ reconfigureHomeLongClick();
+ }
}
};
@@ -506,10 +535,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+ mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
+ R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_FORCE_OPAQUE,
/* defaultValue = */ true);
+ mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ HOME_BUTTON_LONG_PRESS_DURATION_MS,
+ /* defaultValue = */ 0
+ )).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
@@ -769,6 +805,22 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
+ private void reconfigureHomeLongClick() {
+ if (mNavigationBarView == null
+ || mNavigationBarView.getHomeButton().getCurrentView() == null) {
+ return;
+ }
+ if (mHomeButtonLongPressDurationMs.isPresent()) {
+ mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
+ mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
+ mNavigationBarView.getHomeButton().setOnLongClickListener(null);
+ } else {
+ mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true);
+ mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true);
+ mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
+ }
+ }
+
private int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
@@ -779,6 +831,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
pw.println("NavigationBar (displayId=" + mDisplayId + "):");
pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
pw.println(" mCurrentRotation=" + mCurrentRotation);
+ pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
if (mNavigationBarView != null) {
pw.println(" mNavigationBarWindowState="
@@ -1108,7 +1161,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
- homeButton.setOnLongClickListener(this::onHomeLongClick);
+
+ reconfigureHomeLongClick();
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -1118,7 +1172,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
updateScreenPinningGestures();
}
- private boolean onHomeTouch(View v, MotionEvent event) {
+ @VisibleForTesting
+ boolean onHomeTouch(View v, MotionEvent event) {
if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
return true;
}
@@ -1138,9 +1193,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return true;
}
}
+ mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+ });
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mStatusBarLazy.get().awakenDreams();
break;
}
@@ -1468,6 +1527,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ if (!QuickStepContract.isGesturalMode(mode)) {
+ // Reset the override alpha
+ if (getBarTransitions() != null) {
+ getBarTransitions().setBackgroundOverrideAlpha(1f);
+ }
+ }
updateScreenPinningGestures();
if (!canShowSecondaryHandle()) {
@@ -1497,7 +1562,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
public NavigationBarTransitions getBarTransitions() {
- return mNavigationBarView.getBarTransitions();
+ if (mNavigationBarView != null) {
+ return mNavigationBarView.getBarTransitions();
+ }
+ return null;
}
public void finishBarAnimations() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4491cc12a3cb..0bfd065c4b67 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -212,6 +212,14 @@ public class NavigationBarController implements Callbacks,
createNavigationBar(display, null /* savedState */, null /* result */);
}
+ @Override
+ public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ final NavigationBarView navigationBarView = getNavigationBarView(displayId);
+ if (navigationBarView != null) {
+ navigationBarView.setNavigationBarLumaSamplingEnabled(enable);
+ }
+ }
+
/**
* Recreates the navigation bar for the given display.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 61e1d61e7909..fbc7c92a4a64 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -135,6 +136,10 @@ public final class NavigationBarTransitions extends BarTransitions implements
mBarBackground.setFrame(frame);
}
+ void setBackgroundOverrideAlpha(float alpha) {
+ mBarBackground.setOverrideAlpha(alpha);
+ }
+
@Override
protected boolean isLightsOut(int mode) {
return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
@@ -230,4 +235,17 @@ public final class NavigationBarTransitions extends BarTransitions implements
public void removeDarkIntensityListener(DarkIntensityListener listener) {
mDarkIntensityListeners.remove(listener);
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("NavigationBarTransitions:");
+ pw.println(" mMode: " + getMode());
+ pw.println(" mAlwaysOpaque: " + isAlwaysOpaque());
+ pw.println(" mAllowAutoDimWallpaperNotVisible: " + mAllowAutoDimWallpaperNotVisible);
+ pw.println(" mWallpaperVisible: " + mWallpaperVisible);
+ pw.println(" mLightsOut: " + mLightsOut);
+ pw.println(" mAutoDim: " + mAutoDim);
+ pw.println(" bg overrideAlpha: " + mBarBackground.getOverrideAlpha());
+ pw.println(" bg color: " + mBarBackground.getColor());
+ pw.println(" bg frame: " + mBarBackground.getFrame());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 35d5ca949f85..091b17dbb0c0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1158,6 +1158,8 @@ public class NavigationBarView extends FrameLayout implements
int frameHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_frame_height);
mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
+ } else {
+ mBarTransitions.setBackgroundFrame(null);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -1331,6 +1333,7 @@ public class NavigationBarView extends FrameLayout implements
if (mNavigationInflaterView != null) {
mNavigationInflaterView.dump(pw);
}
+ mBarTransitions.dump(pw);
mContextualButtonGroup.dump(pw);
mRecentsOnboarding.dump(pw);
mRegionSamplingHelper.dump(pw);
@@ -1397,4 +1400,12 @@ public class NavigationBarView extends FrameLayout implements
private final Consumer<Rect> mPipListener = bounds -> post(() -> {
mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
});
+
+ void setNavigationBarLumaSamplingEnabled(boolean enable) {
+ if (enable) {
+ mRegionSamplingHelper.start(mSamplingBounds);
+ } else {
+ mRegionSamplingHelper.stop();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 088743cd0f94..9e3be69c414e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -609,20 +609,21 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
if (mVocab != null) {
app = mVocab.getOrDefault(mPackageName, -1);
}
- // Check if we are within the tightest bounds beyond which
- // we would not need to run the ML model.
- boolean withinRange = x < mMLEnableWidth + mLeftInset
- || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
- if (!withinRange) {
+
+ // Denotes whether we should proceed with the gesture. Even if it is false, we may want to
+ // log it assuming it is not invalid due to exclusion.
+ boolean withinRange = x < mEdgeWidthLeft + mLeftInset
+ || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+ if (withinRange) {
int results = -1;
- if (mUseMLModel && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
- withinRange = results == 1;
- } else {
- // Denotes whether we should proceed with the gesture.
- // Even if it is false, we may want to log it assuming
- // it is not invalid due to exclusion.
- withinRange = x < mEdgeWidthLeft + mLeftInset
- || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+
+ // Check if we are within the tightest bounds beyond which we would not need to run the
+ // ML model
+ boolean withinMinRange = x < mMLEnableWidth + mLeftInset
+ || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
+ if (!withinMinRange && mUseMLModel
+ && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
+ withinRange = (results == 1);
}
}
@@ -726,7 +727,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mGestureLog.removeFirst();
}
mGestureLog.addLast(String.format(
- "Gesture [%d,alw=%B,%B, %B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
+ "Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed,
QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index e7458a3df801..6295692c788c 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -110,16 +110,16 @@ public class PeopleProvider extends ContentProvider {
}
if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId);
- RemoteViews view = PeopleSpaceUtils.createRemoteViews(getContext(), tile, 0);
final Bundle bundle = new Bundle();
+ RemoteViews view = PeopleSpaceUtils.createRemoteViews(getContext(), tile, 0, bundle);
bundle.putParcelable(PeopleProviderUtils.RESPONSE_KEY_REMOTE_VIEWS, view);
return bundle;
}
private boolean doesCallerHavePermission() {
return getContext().checkPermission(
- PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_PERMISSION,
- Binder.getCallingPid(), Binder.getCallingUid())
+ PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_PERMISSION,
+ Binder.getCallingPid(), Binder.getCallingUid())
== PackageManager.PERMISSION_GRANTED;
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a69ec278be91..378e49deb699 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
+import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.List;
@@ -117,7 +118,7 @@ public class PeopleSpaceActivity extends Activity {
String pkg = tile.getPackageName();
String status =
PeopleSpaceUtils.getLastInteractionString(mContext,
- tile.getLastInteractionTimestamp(), true);
+ tile.getLastInteractionTimestamp());
tileView.setStatus(status);
tileView.setName(tile.getUserName().toString());
@@ -138,7 +139,9 @@ public class PeopleSpaceActivity extends Activity {
+ mAppWidgetId);
}
}
- mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId);
+ PeopleTileKey key = new PeopleTileKey(
+ tile.getId(), tile.getUserHandle().getIdentifier(), tile.getPackageName());
+ mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key);
finishActivity();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 502c95c47d03..e07e9cff4e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -26,6 +26,10 @@ import static android.app.people.ConversationStatus.ACTIVITY_MEDIA;
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
import static android.app.people.ConversationStatus.ACTIVITY_UPCOMING_BIRTHDAY;
import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import android.annotation.Nullable;
import android.app.INotificationManager;
@@ -41,6 +45,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
+import android.content.res.Configuration;
import android.database.Cursor;
import android.database.SQLException;
import android.graphics.Bitmap;
@@ -54,13 +59,14 @@ import android.icu.util.MeasureUnit;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
-import android.os.ServiceManager;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.IconDrawableFactory;
import android.util.Log;
+import android.util.TypedValue;
import android.view.View;
import android.widget.RemoteViews;
@@ -71,10 +77,11 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.people.widget.AppWidgetOptionsHelper;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -106,15 +113,23 @@ public class PeopleSpaceUtils {
private static final int DAYS_IN_A_WEEK = 7;
private static final int MIN_HOUR = 1;
private static final int ONE_DAY = 1;
- public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
public static final String PACKAGE_NAME = "package_name";
public static final String USER_ID = "user_id";
public static final String SHORTCUT_ID = "shortcut_id";
public static final String EMPTY_STRING = "";
- public static final int INVALID_WIDGET_ID = -1;
public static final int INVALID_USER_ID = -1;
+ public static final PeopleTileKey EMPTY_KEY =
+ new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING);
+ public static final int SMALL_LAYOUT = 0;
+ public static final int MEDIUM_LAYOUT = 1;
+ @VisibleForTesting
+ static final int REQUIRED_WIDTH_FOR_MEDIUM = 146;
+ private static final int AVATAR_SIZE_FOR_MEDIUM = 56;
+ private static final int DEFAULT_WIDTH = 146;
+ private static final int DEFAULT_HEIGHT = 92;
+
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
@@ -200,67 +215,77 @@ public class PeopleSpaceUtils {
AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
for (int appWidgetId : appWidgetIds) {
- PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
- appWidgetId);
+ PeopleSpaceTile tile = getPeopleSpaceTile(
+ context, appWidgetId, appWidgetManager, peopleManager);
if (tile == null) {
if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
//TODO: Delete app widget id when crash is fixed (b/172932636)
continue;
}
-
- if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
- RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
- // Tell the AppWidgetManager to perform an update on the current app widget.
- appWidgetManager.updateAppWidget(appWidgetId, views);
-
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ updateAppWidgetViews(appWidgetManager, context, appWidgetId, tile, options);
widgetIdToTile.put(appWidgetId, tile);
}
getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
}
+ /**
+ * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. If the PeopleSpaceTile
+ * isn't cached, store it in AppWidgetOptions.
+ */
@Nullable
- public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
- AppWidgetManager appWidgetManager,
- Context context, int appWidgetId) {
- try {
- // Migrate storage for existing users.
- SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
- Context.MODE_PRIVATE);
- String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
- int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
- String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
- if (!validKey(shortcutId, pkg, userId)) {
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
- shortcutId = sp.getString(String.valueOf(appWidgetId), null);
- if (shortcutId == null) {
- Log.e(TAG, "Cannot restore widget");
- return null;
- }
- migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
- pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
- userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
- }
+ public static PeopleSpaceTile getPeopleSpaceTile(Context context, int appWidgetId,
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
+ // First, check if tile is cached in AppWidgetOptions.
+ PeopleSpaceTile tile = AppWidgetOptionsHelper.getPeopleTile(appWidgetManager, appWidgetId);
+ if (tile != null) {
+ if (DEBUG) Log.d(TAG, "People Tile is cached for widget: " + appWidgetId);
+ return tile;
+ }
- // Check if tile is cached.
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
- if (tile != null) {
- return tile;
- }
+ // If not, we get the PeopleTileKey from SharedPreferences, retrieve the Conversation from
+ // persisted storage, and cache it in AppWidgetOptions.
+ SharedPreferences widgetSp = context.getSharedPreferences(
+ String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ PeopleTileKey sharedPreferencesKey = new PeopleTileKey(
+ widgetSp.getString(SHORTCUT_ID, EMPTY_STRING),
+ widgetSp.getInt(USER_ID, INVALID_USER_ID),
+ widgetSp.getString(PACKAGE_NAME, EMPTY_STRING));
- // If tile is null, we need to retrieve from persisted storage.
- if (DEBUG) {
- Log.d(TAG,
- "Retrieving from storage after reboots: " + shortcutId + " user: " + userId
- + " pkg: " + pkg);
- }
+ if (!sharedPreferencesKey.isValid()) {
+ Log.e(TAG, "Cannot find shortcut info for widgetId: " + appWidgetId);
+ return null;
+ }
+
+ if (DEBUG) Log.d(TAG, "PeopleTile key is present in sharedPreferences: " + appWidgetId);
+ // If tile is null, we need to retrieve from persisted storage.
+ return getPeopleTileFromPersistentStorage(context, sharedPreferencesKey, peopleManager);
+ }
+
+ /**
+ * Returns a {@link PeopleSpaceTile} based on {@link ConversationChannel} returned by
+ * {@link IPeopleManager}.
+ */
+ public static PeopleSpaceTile getPeopleTileFromPersistentStorage(Context context,
+ PeopleTileKey peopleTileKey, IPeopleManager peopleManager) {
+ try {
+ if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + peopleTileKey.toString());
LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+ if (launcherApps == null) {
+ Log.d(TAG, "LauncherApps is null");
+ return null;
+ }
+
+ ConversationChannel channel = peopleManager.getConversation(
+ peopleTileKey.getPackageName(),
+ peopleTileKey.getUserId(),
+ peopleTileKey.getShortcutId());
if (channel == null) {
Log.d(TAG, "Could not retrieve conversation from storage");
return null;
}
+
return new PeopleSpaceTile.Builder(channel, launcherApps).build();
} catch (Exception e) {
Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
@@ -269,79 +294,46 @@ public class PeopleSpaceUtils {
}
/** Returns stored widgets for the conversation specified. */
- public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId,
- String packageName, int userId) {
- if (shortcutId == null || packageName == null) {
+ public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) {
+ if (!key.isValid()) {
return new HashSet<>();
}
- String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
- return new HashSet<>(sp.getStringSet(key, new HashSet<>()));
- }
-
-
- /** Best-effort attempts to migrate existing users to the new storage format. */
- // TODO: Remove after sufficient time. Temporary migration storage for existing users.
- private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
- int appWidgetId) {
- try {
- List<PeopleSpaceTile> tiles =
- PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
- IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE)),
- context.getSystemService(LauncherApps.class),
- Dependency.get(NotificationEntryManager.class));
- Optional<PeopleSpaceTile> entry = tiles.stream().filter(
- e -> e.getId().equals(shortcutId)).findFirst();
- if (entry.isPresent()) {
- if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
- setStorageForTile(context, entry.get(), appWidgetId);
- } else {
- Log.e(TAG, "Could not migrate user. Delete old storage");
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
- SharedPreferences.Editor editor = sp.edit();
- editor.remove(String.valueOf(appWidgetId));
- editor.apply();
- }
- } catch (Exception e) {
- Log.e(TAG, "Could not query conversations");
- }
+ return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>()));
}
/** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
- public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
+ int appWidgetId) {
// Write relevant persisted storage.
SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
Context.MODE_PRIVATE);
SharedPreferences.Editor widgetEditor = widgetSp.edit();
- widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
- widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
- int userId = getUserId(tile);
- widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName());
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId());
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId());
widgetEditor.apply();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(appWidgetId), tile.getId());
- String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+ editor.putString(String.valueOf(appWidgetId), key.getShortcutId());
+
// Don't overwrite existing widgets with the same key.
- Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ Set<String> storedWidgetIds = new HashSet<>(
+ sp.getStringSet(key.toString(), new HashSet<>()));
storedWidgetIds.add(String.valueOf(appWidgetId));
- editor.putStringSet(key, storedWidgetIds);
+ editor.putStringSet(key.toString(), storedWidgetIds);
editor.apply();
-
- // Write cached storage.
- updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
- tile);
}
/** Removes stored data when tile is deleted. */
- public static void removeStorageForTile(Context context, String key, int widgetId) {
+ public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
+ int widgetId) {
// Delete widgetId mapping to key.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
- Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ Set<String> storedWidgetIds = new HashSet<>(
+ sp.getStringSet(key.toString(), new HashSet<>()));
storedWidgetIds.remove(String.valueOf(widgetId));
- editor.putStringSet(key, storedWidgetIds);
+ editor.putStringSet(key.toString(), storedWidgetIds);
editor.remove(String.valueOf(widgetId));
editor.apply();
@@ -361,12 +353,15 @@ public class PeopleSpaceUtils {
Log.w(TAG, "NotificationEntryManager is null");
return tiles;
}
- Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
+ Map<PeopleTileKey, NotificationEntry> visibleNotifications = notificationEntryManager
.getVisibleNotifications()
.stream()
.filter(entry -> entry.getRanking() != null
&& entry.getRanking().getConversationShortcutInfo() != null)
- .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
+ .collect(Collectors.toMap(PeopleTileKey::new, e -> e,
+ // Handle duplicate keys to avoid crashes.
+ (e1, e2) -> e1.getSbn().getNotification().when
+ > e2.getSbn().getNotification().when ? e1 : e2));
if (DEBUG) {
Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
}
@@ -378,16 +373,15 @@ public class PeopleSpaceUtils {
}
static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context,
- PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
- String shortcutId = tile.getId();
- String packageName = tile.getPackageName();
- int userId = getUserId(tile);
- String key = getKey(shortcutId, packageName, userId);
+ PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) {
+ PeopleTileKey key = new PeopleTileKey(
+ tile.getId(), getUserId(tile), tile.getPackageName());
+
if (!visibleNotifications.containsKey(key)) {
- if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
+ if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString());
return tile;
}
- if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
+ if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key.toString());
return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
}
@@ -423,22 +417,13 @@ public class PeopleSpaceUtils {
.build();
}
- private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
- PeopleSpaceTile tile) {
- if (tile == null) {
- if (DEBUG) Log.d(TAG, "Requested to store null tile");
- return;
- }
- Bundle newOptions = new Bundle();
- newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, tile);
- appWidgetManager.updateAppWidgetOptions(appWidgetId, newOptions);
- }
-
/** Creates a {@link RemoteViews} for {@code tile}. */
public static RemoteViews createRemoteViews(Context context,
- PeopleSpaceTile tile, int appWidgetId) {
- RemoteViews viewsForTile = getViewForTile(context, tile);
- RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile);
+ PeopleSpaceTile tile, int appWidgetId, Bundle options) {
+ int layoutSize = getLayoutSize(context, options);
+ RemoteViews viewsForTile = getViewForTile(context, tile, layoutSize);
+ int maxAvatarSize = getMaxAvatarSize(context, options, layoutSize);
+ RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile, maxAvatarSize);
return setLaunchIntents(context, views, tile, appWidgetId);
}
@@ -446,15 +431,16 @@ public class PeopleSpaceUtils {
* The prioritization for the {@code tile} content is missed calls, followed by notification
* content, then birthdays, then the most recent status, and finally last interaction.
*/
- private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile) {
+ private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile,
+ int layoutSize) {
if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
if (DEBUG) Log.d(TAG, "Create missed call view");
- return createMissedCallRemoteViews(context, tile);
+ return createMissedCallRemoteViews(context, tile, layoutSize);
}
if (tile.getNotificationKey() != null) {
if (DEBUG) Log.d(TAG, "Create notification view");
- return createNotificationRemoteViews(context, tile);
+ return createNotificationRemoteViews(context, tile, layoutSize);
}
// TODO: Add sorting when we expose timestamp of statuses.
@@ -464,7 +450,7 @@ public class PeopleSpaceUtils {
ConversationStatus birthdayStatus = getBirthdayStatus(tile, statusesForEntireView);
if (birthdayStatus != null) {
if (DEBUG) Log.d(TAG, "Create birthday view");
- return createStatusRemoteViews(context, birthdayStatus);
+ return createStatusRemoteViews(context, birthdayStatus, layoutSize);
}
if (!statusesForEntireView.isEmpty()) {
@@ -472,10 +458,48 @@ public class PeopleSpaceUtils {
Log.d(TAG,
"Create status view for: " + statusesForEntireView.get(0).getActivity());
}
- return createStatusRemoteViews(context, statusesForEntireView.get(0));
+ return createStatusRemoteViews(context, statusesForEntireView.get(0), layoutSize);
+ }
+
+ return createLastInteractionRemoteViews(context, tile, layoutSize);
+ }
+
+ /** Calculates the best layout relative to the size in {@code options}. */
+ private static int getLayoutSize(Context context, Bundle options) {
+ int display = context.getResources().getConfiguration().orientation;
+ int width = display == Configuration.ORIENTATION_PORTRAIT
+ ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt(
+ OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH);
+ int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
+ OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT)
+ : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT);
+ // Small layout used below a certain minimum width with any height.
+ if (width < REQUIRED_WIDTH_FOR_MEDIUM) {
+ if (DEBUG) Log.d(TAG, "Small view for width: " + width + " height: " + height);
+ return SMALL_LAYOUT;
}
+ if (DEBUG) Log.d(TAG, "Medium view for width: " + width + " height: " + height);
+ return MEDIUM_LAYOUT;
+ }
- return createLastInteractionRemoteViews(context, tile);
+ /** Returns the max avatar size for {@code layoutSize} under the current {@code options}. */
+ private static int getMaxAvatarSize(Context context, Bundle options, int layoutSize) {
+ int avatarHeightSpace = AVATAR_SIZE_FOR_MEDIUM;
+ int avatarWidthSpace = AVATAR_SIZE_FOR_MEDIUM;
+
+ if (layoutSize == SMALL_LAYOUT) {
+ int display = context.getResources().getConfiguration().orientation;
+ int width = display == Configuration.ORIENTATION_PORTRAIT
+ ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt(
+ OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH);
+ int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
+ OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT)
+ : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT);
+ avatarHeightSpace = height - (8 + 4 + 18 + 8);
+ avatarWidthSpace = width - (4 + 4);
+ }
+ if (DEBUG) Log.d(TAG, "Height: " + avatarHeightSpace + " width: " + avatarWidthSpace);
+ return Math.min(avatarHeightSpace, avatarWidthSpace);
}
@Nullable
@@ -513,14 +537,22 @@ public class PeopleSpaceUtils {
}
}
- private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status) {
- RemoteViews views = new RemoteViews(
- context.getPackageName(), R.layout.people_space_small_avatar_tile);
+ private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status,
+ int layoutSize) {
+ int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
+ : R.layout.people_space_small_avatar_tile;
+ RemoteViews views = new RemoteViews(context.getPackageName(), layout);
CharSequence statusText = status.getDescription();
if (TextUtils.isEmpty(statusText)) {
statusText = getStatusTextByType(context, status.getActivity());
}
- views.setTextViewText(R.id.status, statusText);
+ views.setViewVisibility(R.id.subtext, View.GONE);
+ views.setViewVisibility(R.id.text_content, View.VISIBLE);
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
+ int secondaryTextColor = context.getColor(typedValue.resourceId);
+ views.setInt(R.id.text_content, "setTextColor", secondaryTextColor);
+ views.setTextViewText(R.id.text_content, statusText);
Icon statusIcon = status.getIcon();
if (statusIcon != null) {
views.setImageViewIcon(R.id.image, statusIcon);
@@ -529,6 +561,8 @@ public class PeopleSpaceUtils {
views.setViewVisibility(R.id.content_background, View.GONE);
}
// TODO: Set status pre-defined icons
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
+ ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
return views;
}
@@ -554,7 +588,7 @@ public class PeopleSpaceUtils {
}
private static RemoteViews setCommonRemoteViewsFields(Context context, RemoteViews views,
- PeopleSpaceTile tile) {
+ PeopleSpaceTile tile, int maxAvatarSize) {
try {
boolean isAvailable =
tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
@@ -567,27 +601,20 @@ public class PeopleSpaceUtils {
boolean hasNewStory =
tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
c -> c.getActivity() == ACTIVITY_NEW_STORY);
- if (hasNewStory) {
- views.setViewVisibility(R.id.person_icon_with_story, View.VISIBLE);
- views.setViewVisibility(R.id.person_icon_only, View.GONE);
- views.setImageViewIcon(R.id.person_icon_inside_ring, tile.getUserIcon());
- } else {
- views.setViewVisibility(R.id.person_icon_with_story, View.GONE);
- views.setViewVisibility(R.id.person_icon_only, View.VISIBLE);
- views.setImageViewIcon(R.id.person_icon_only, tile.getUserIcon());
- }
-
views.setTextViewText(R.id.name, tile.getUserName().toString());
- views.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
views.setBoolean(R.id.content_background, "setClipToOutline", true);
+ Icon icon = tile.getUserIcon();
+ PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context,
+ context.getPackageManager(),
+ IconDrawableFactory.newInstance(context, false),
+ maxAvatarSize);
+ Drawable drawable = icon.loadDrawable(context);
+ Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable,
+ tile.getPackageName(), getUserId(tile), tile.isImportantConversation(),
+ hasNewStory);
+ Bitmap bitmap = convertDrawableToBitmap(personDrawable);
+ views.setImageViewBitmap(R.id.person_icon, bitmap);
- views.setImageViewBitmap(
- R.id.package_icon,
- PeopleSpaceUtils.convertDrawableToBitmap(
- context.getPackageManager().getApplicationIcon(
- tile.getPackageName())
- )
- );
return views;
} catch (Exception e) {
Log.e(TAG, "Failed to set common fields: " + e);
@@ -620,49 +647,79 @@ public class PeopleSpaceUtils {
} catch (Exception e) {
Log.e(TAG, "Failed to add launch intents: " + e);
}
+
return views;
}
private static RemoteViews createMissedCallRemoteViews(Context context,
- PeopleSpaceTile tile) {
- RemoteViews views = new RemoteViews(
- context.getPackageName(), R.layout.people_space_small_avatar_tile);
- views.setTextViewText(R.id.status, tile.getNotificationContent());
- views.setImageViewResource(R.id.status_defined_icon, R.drawable.ic_phone_missed);
+ PeopleSpaceTile tile, int layoutSize) {
+ int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
+ : R.layout.people_space_small_avatar_tile;
+ RemoteViews views = new RemoteViews(context.getPackageName(), layout);
+ views.setViewVisibility(R.id.subtext, View.GONE);
+ views.setViewVisibility(R.id.text_content, View.VISIBLE);
+ views.setTextViewText(R.id.text_content, tile.getNotificationContent());
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
+ ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
views.setBoolean(R.id.content_background, "setClipToOutline", true);
return views;
}
+ private static void ensurePredefinedIconVisibleOnSmallView(RemoteViews views, int layoutSize) {
+ if (layoutSize == SMALL_LAYOUT) {
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+ }
+ }
+
private static RemoteViews createNotificationRemoteViews(Context context,
- PeopleSpaceTile tile) {
- RemoteViews views = new RemoteViews(
- context.getPackageName(), R.layout.people_space_notification_content_tile);
+ PeopleSpaceTile tile, int layoutSize) {
+ int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
+ : R.layout.people_space_small_avatar_tile;
+ RemoteViews views = new RemoteViews(context.getPackageName(), layout);
+ if (layoutSize != MEDIUM_LAYOUT) {
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+ }
Uri image = tile.getNotificationDataUri();
+ ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
if (image != null) {
// TODO: Use NotificationInlineImageCache
views.setImageViewUri(R.id.image, image);
views.setViewVisibility(R.id.content_background, View.VISIBLE);
views.setBoolean(R.id.content_background, "setClipToOutline", true);
- views.setViewVisibility(R.id.content, View.GONE);
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera);
} else {
CharSequence content = tile.getNotificationContent();
views = setPunctuationRemoteViewsFields(views, content);
- views.setTextViewText(R.id.content, content);
- views.setViewVisibility(R.id.content, View.VISIBLE);
+ views.setTextViewText(R.id.text_content, tile.getNotificationContent());
+ // TODO: Measure max lines from height.
+ views.setInt(R.id.text_content, "setMaxLines", 2);
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
+ int primaryTextColor = context.getColor(typedValue.resourceId);
+ views.setInt(R.id.text_content, "setTextColor", primaryTextColor);
+ views.setViewVisibility(R.id.text_content, View.VISIBLE);
views.setViewVisibility(R.id.content_background, View.GONE);
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
}
// TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile.
- views.setTextViewText(R.id.subtext, PeopleSpaceUtils.getLastInteractionString(
- context, tile.getLastInteractionTimestamp(), false));
+ views.setViewVisibility(R.id.subtext, View.GONE);
return views;
}
private static RemoteViews createLastInteractionRemoteViews(Context context,
- PeopleSpaceTile tile) {
- RemoteViews views = new RemoteViews(
- context.getPackageName(), R.layout.people_space_large_avatar_tile);
+ PeopleSpaceTile tile, int layoutSize) {
+ int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
+ : R.layout.people_space_large_avatar_tile;
+ RemoteViews views = new RemoteViews(context.getPackageName(), layout);
+ if (layoutSize == SMALL_LAYOUT) {
+ views.setViewVisibility(R.id.name, View.VISIBLE);
+ views.setViewVisibility(R.id.predefined_icon, View.GONE);
+ }
String status = PeopleSpaceUtils.getLastInteractionString(
- context, tile.getLastInteractionTimestamp(), true);
+ context, tile.getLastInteractionTimestamp());
views.setTextViewText(R.id.last_interaction, status);
return views;
}
@@ -808,8 +865,7 @@ public class PeopleSpaceUtils {
}
/** Returns a readable status describing the {@code lastInteraction}. */
- public static String getLastInteractionString(Context context, long lastInteraction,
- boolean includeLastChatted) {
+ public static String getLastInteractionString(Context context, long lastInteraction) {
if (lastInteraction == 0L) {
Log.e(TAG, "Could not get valid last interaction");
return context.getString(R.string.basic_status);
@@ -818,41 +874,20 @@ public class PeopleSpaceUtils {
Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
MeasureFormat.FormatWidth.WIDE);
- MeasureFormat shortFormatter = MeasureFormat.getInstance(Locale.getDefault(),
- MeasureFormat.FormatWidth.SHORT);
if (durationSinceLastInteraction.toHours() < MIN_HOUR) {
- if (includeLastChatted) {
- return context.getString(R.string.last_interaction_status_less_than,
- formatter.formatMeasures(new Measure(MIN_HOUR, MeasureUnit.HOUR)));
- }
- return context.getString(R.string.timestamp, shortFormatter.formatMeasures(
+ return context.getString(R.string.timestamp, formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toMinutes(), MeasureUnit.MINUTE)));
} else if (durationSinceLastInteraction.toDays() < ONE_DAY) {
- if (includeLastChatted) {
- return context.getString(R.string.last_interaction_status,
- formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toHours(),
- MeasureUnit.HOUR)));
- }
- return context.getString(R.string.timestamp, shortFormatter.formatMeasures(
+ return context.getString(R.string.timestamp, formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toHours(),
MeasureUnit.HOUR)));
} else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
- if (includeLastChatted) {
- return context.getString(R.string.last_interaction_status,
- formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toDays(),
- MeasureUnit.DAY)));
- }
- return context.getString(R.string.timestamp, shortFormatter.formatMeasures(
+ return context.getString(R.string.timestamp, formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toHours(),
MeasureUnit.DAY)));
} else {
return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
- ? (includeLastChatted ? R.string.last_interaction_status :
- R.string.timestamp) :
- (includeLastChatted ? R.string.last_interaction_status_over
- : R.string.over_timestamp),
+ ? R.string.timestamp : R.string.over_timestamp,
formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
MeasureUnit.WEEK)));
@@ -957,12 +992,20 @@ public class PeopleSpaceUtils {
removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId);
}
- /** Update app widget options and the current view. */
+ private static void updateAppWidgetViews(AppWidgetManager appWidgetManager,
+ Context context, int appWidgetId, PeopleSpaceTile tile, Bundle options) {
+ if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+ RemoteViews views = createRemoteViews(context, tile, appWidgetId, options);
+
+ // Tell the AppWidgetManager to perform an update on the current app widget.
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+
+ /** Updates tile in app widget options and the current view. */
public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
Context context, int appWidgetId, PeopleSpaceTile tile) {
- updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
- RemoteViews views = createRemoteViews(context, tile, appWidgetId);
- appWidgetManager.updateAppWidget(appWidgetId, views);
+ Bundle options = AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile);
+ updateAppWidgetViews(appWidgetManager, context, appWidgetId, tile, options);
}
/**
@@ -1006,44 +1049,6 @@ public class PeopleSpaceUtils {
return lookupKeysWithBirthdaysToday;
}
- static String getKey(NotificationEntry entry) {
- if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null
- || entry.getSbn() == null || entry.getSbn().getUser() == null) {
- return null;
- }
- return getKey(entry.getRanking().getConversationShortcutInfo().getId(),
- entry.getSbn().getPackageName(),
- entry.getSbn().getUser().getIdentifier());
- }
-
- /**
- * Returns the uniquely identifying key for the conversation.
- *
- * <p>{@code userId} will always be a number, so we put user ID as the
- * delimiter between the app-provided strings of shortcut ID and package name.
- *
- * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
- * a {@code packageName} to always start with a letter. This restriction means we are
- * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
- * case is impossible given the package name restrictions:
- * <ul>
- * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
- * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
- * </ul>
- */
- @Nullable
- public static String getKey(String shortcutId, String packageName, int userId) {
- if (!validKey(shortcutId, packageName, userId)) {
- return null;
- }
- return shortcutId + "/" + userId + "/" + packageName;
- }
-
- /** Returns whether the key is valid. */
- public static boolean validKey(String shortcutId, String packageName, int userId) {
- return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0;
- }
-
/** Returns the userId associated with a {@link PeopleSpaceTile} */
public static int getUserId(PeopleSpaceTile tile) {
return tile.getUserHandle().getIdentifier();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
new file mode 100644
index 000000000000..145fee5e762a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.people;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.TypedValue;
+
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.systemui.R;
+
+class PeopleStoryIconFactory extends BaseIconFactory {
+
+ private static final int PADDING = 2;
+ private static final int RING_WIDTH = 2;
+ private static final int MAX_BADGE_SIZE = 36;
+
+ final PackageManager mPackageManager;
+ final IconDrawableFactory mIconDrawableFactory;
+ private int mImportantConversationColor;
+ private int mAccentColor;
+ private float mDensity;
+ private float mIconSize;
+
+ PeopleStoryIconFactory(Context context, PackageManager pm,
+ IconDrawableFactory iconDrawableFactory, int iconSizeDp) {
+ super(context, context.getResources().getConfiguration().densityDpi,
+ (int) (iconSizeDp * context.getResources().getDisplayMetrics().density));
+ mDensity = context.getResources().getDisplayMetrics().density;
+ mIconSize = mDensity * iconSizeDp;
+ mPackageManager = pm;
+ mIconDrawableFactory = iconDrawableFactory;
+ mImportantConversationColor = context.getColor(R.color.important_conversation);
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+ mAccentColor = context.getColor(typedValue.resourceId);
+ }
+
+
+ /**
+ * Gets the {@link Drawable} that represents the app icon, badged with the work profile icon
+ * if appropriate.
+ */
+ private Drawable getAppBadge(String packageName, int userId) {
+ Drawable badge = null;
+ try {
+ final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.GET_META_DATA, userId);
+ badge = mIconDrawableFactory.getBadgedIcon(appInfo, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ badge = mPackageManager.getDefaultActivityIcon();
+ }
+ return badge;
+ }
+
+ /**
+ * Returns a {@link Drawable} for the entire conversation. The shortcut icon will be badged
+ * with the launcher icon of the app specified by packageName.
+ */
+ public Drawable getPeopleTileDrawable(Drawable headDrawable, String packageName, int userId,
+ boolean important, boolean newStory) {
+ return new PeopleStoryIconDrawable(headDrawable, getAppBadge(packageName, userId),
+ mIconBitmapSize, mImportantConversationColor, important, mIconSize, mDensity,
+ mAccentColor, newStory);
+ }
+
+ /**
+ * Custom drawable which overlays a badge drawable on a head icon (conversation/person avatar),
+ * with decorations indicating Important conversations and having a New Story.
+ */
+ public static class PeopleStoryIconDrawable extends Drawable {
+ private float mFullIconSize;
+ private Drawable mAvatar;
+ private Drawable mBadgeIcon;
+ private int mIconSize;
+ private Paint mPriorityRingPaint;
+ private boolean mShowImportantRing;
+ private boolean mShowStoryRing;
+ private Paint mStoryPaint;
+ private float mDensity;
+
+ PeopleStoryIconDrawable(Drawable avatar,
+ Drawable badgeIcon,
+ int iconSize,
+ @ColorInt int ringColor,
+ boolean showImportantRing, float fullIconSize, float density,
+ @ColorInt int accentColor, boolean showStoryRing) {
+ mAvatar = avatar;
+ mBadgeIcon = badgeIcon;
+ mIconSize = iconSize;
+ mShowImportantRing = showImportantRing;
+ mPriorityRingPaint = new Paint();
+ mPriorityRingPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ mPriorityRingPaint.setColor(ringColor);
+ mShowStoryRing = showStoryRing;
+ mStoryPaint = new Paint();
+ mStoryPaint.setStyle(Paint.Style.STROKE);
+ mStoryPaint.setColor(accentColor);
+ mFullIconSize = fullIconSize;
+ mDensity = density;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mIconSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mIconSize;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final Rect bounds = getBounds();
+ final int minBound = Math.min(bounds.height(), bounds.width());
+ // Scale head icon and app icon to our canvas.
+ float scale = minBound / mFullIconSize;
+
+ int paddingInDp = (int) (PADDING * mDensity);
+ int ringStrokeWidth = (int) (RING_WIDTH * mDensity);
+ mPriorityRingPaint.setStrokeWidth(ringStrokeWidth);
+ mStoryPaint.setStrokeWidth(ringStrokeWidth);
+
+ int scaledFullIconSize = (int) (mFullIconSize * scale);
+ int avatarSize = scaledFullIconSize - (paddingInDp * 2);
+ if (mAvatar != null) {
+ int leftAndTopPadding = paddingInDp;
+ int rightAndBottomPadding = avatarSize + paddingInDp;
+ if (mShowStoryRing) {
+ int headCenter = scaledFullIconSize / 2;
+ canvas.drawCircle(headCenter, headCenter,
+ getRadius(avatarSize, ringStrokeWidth),
+ mStoryPaint);
+ leftAndTopPadding += (ringStrokeWidth + paddingInDp);
+ rightAndBottomPadding -= (ringStrokeWidth + paddingInDp);
+ }
+ mAvatar.setBounds(leftAndTopPadding,
+ leftAndTopPadding,
+ rightAndBottomPadding,
+ rightAndBottomPadding);
+ mAvatar.draw(canvas);
+ } else {
+ Log.w("PeopleStoryIconFactory", "Null avatar icon");
+ }
+
+ // Determine badge size from either the size relative to the head icon, or max size.
+ int maxBadgeSize = (int) (MAX_BADGE_SIZE * mDensity);
+ int badgeSizeRelativeToHead = (int) (avatarSize / 2.4);
+ int badgeSize = Math.min(maxBadgeSize, badgeSizeRelativeToHead);
+ if (mBadgeIcon != null) {
+ int leftAndTopPadding = scaledFullIconSize - badgeSize;
+ int rightAndBottomPadding = scaledFullIconSize;
+ if (mShowImportantRing) {
+ int badgeCenter = leftAndTopPadding + (badgeSize / 2);
+ canvas.drawCircle(badgeCenter, badgeCenter,
+ getRadius(badgeSize, ringStrokeWidth),
+ mPriorityRingPaint);
+ leftAndTopPadding += ringStrokeWidth;
+ rightAndBottomPadding -= ringStrokeWidth;
+ }
+ mBadgeIcon.setBounds(
+ leftAndTopPadding,
+ leftAndTopPadding,
+ rightAndBottomPadding,
+ rightAndBottomPadding);
+ mBadgeIcon.draw(canvas);
+ } else {
+ Log.w("PeopleStoryIconFactory", "Null badge icon");
+ }
+ }
+
+ private int getRadius(int circleWidth, int circleStrokeWidth) {
+ return (circleWidth - circleStrokeWidth) / 2;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // unimplemented
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // unimplemented
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java
new file mode 100644
index 000000000000..7254eec71d07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.people.widget;
+
+import static com.android.systemui.people.PeopleSpaceUtils.DEBUG;
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_KEY;
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
+
+import android.app.people.PeopleSpaceTile;
+import android.appwidget.AppWidgetManager;
+import android.os.Bundle;
+import android.util.Log;
+
+/** Helper class encapsulating AppWidgetOptions for People Tile. */
+public class AppWidgetOptionsHelper {
+ private static final String TAG = "AppWidgetOptionsHelper";
+
+ /** Key to store {@link PeopleSpaceTile} in AppWidgetOptions Bundle. */
+ public static final String OPTIONS_PEOPLE_TILE = "options_people_tile";
+
+ /** Sets {@link PeopleSpaceTile} in AppWidgetOptions. */
+ public static Bundle setPeopleTile(AppWidgetManager appWidgetManager, int appWidgetId,
+ PeopleSpaceTile tile) {
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ if (tile == null) {
+ if (DEBUG) Log.d(TAG, "Requested to store null tile");
+ return options;
+ }
+ options.putParcelable(OPTIONS_PEOPLE_TILE, tile);
+ appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
+ return options;
+ }
+
+ /** Gets {@link PeopleSpaceTile} from AppWidgetOptions. */
+ public static PeopleSpaceTile getPeopleTile(AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ return options != null ? options.getParcelable(OPTIONS_PEOPLE_TILE) : null;
+ }
+
+ /** Sets {@link PeopleTileKey} in AppWidgetOptions. */
+ public static void setPeopleTileKey(AppWidgetManager appWidgetManager, int appWidgetId,
+ PeopleTileKey key) {
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ options.putString(SHORTCUT_ID, key.getShortcutId());
+ options.putInt(USER_ID, key.getUserId());
+ options.putString(PACKAGE_NAME, key.getPackageName());
+ appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
+ }
+
+ /** Gets {@link PeopleTileKey} from AppWidgetOptions. */
+ public static PeopleTileKey getPeopleTileKey(AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ if (options == null) {
+ return EMPTY_KEY;
+ }
+ return getPeopleTileKeyFromBundle(options);
+ }
+
+ /** Gets {@link PeopleTileKey} from Bundle {@code options}. */
+ public static PeopleTileKey getPeopleTileKeyFromBundle(Bundle options) {
+ String pkg = options.getString(PACKAGE_NAME, EMPTY_STRING);
+ int userId = options.getInt(USER_ID, INVALID_USER_ID);
+ String shortcutId = options.getString(SHORTCUT_ID, EMPTY_STRING);
+ return new PeopleTileKey(shortcutId, userId, pkg);
+ }
+
+ /** Removes {@link PeopleTileKey} from AppWidgetOptions. */
+ public static void removePeopleTileKey(AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+ setPeopleTileKey(appWidgetManager, appWidgetId, EMPTY_KEY);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 22ee9e89d0a0..30eb2ac160c7 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.people.widget;
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
@@ -38,6 +39,7 @@ import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceManager;
@@ -78,8 +80,8 @@ public class PeopleSpaceWidgetManager {
private PeopleManager mPeopleManager;
public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@GuardedBy("mLock")
- public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners =
- new HashMap<>();
+ public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener>
+ mListeners = new HashMap<>();
@Inject
public PeopleSpaceWidgetManager(Context context) {
@@ -122,8 +124,8 @@ public class PeopleSpaceWidgetManager {
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
synchronized (mLock) {
- PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
- mAppWidgetManager, mIPeopleManager);
+ PeopleSpaceUtils.updateSingleConversationWidgets(
+ mContext, widgetIds, mAppWidgetManager, mIPeopleManager);
}
}
} catch (Exception e) {
@@ -157,9 +159,11 @@ public class PeopleSpaceWidgetManager {
return;
}
synchronized (mLock) {
- Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId,
- sbn.getPackageName(),
- UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier());
+ PeopleTileKey key = new PeopleTileKey(
+ sbnShortcutId,
+ UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(),
+ sbn.getPackageName());
+ Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key);
for (String widgetIdString : storedWidgetIds) {
int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
@@ -177,9 +181,9 @@ public class PeopleSpaceWidgetManager {
public void updateWidgetsWithConversationChanged(ConversationChannel conversation) {
ShortcutInfo info = conversation.getShortcutInfo();
synchronized (mLock) {
- Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(),
- info.getPackage(),
- info.getUserId());
+ PeopleTileKey key = new PeopleTileKey(
+ info.getId(), info.getUserId(), info.getPackage());
+ Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key);
for (String widgetIdString : storedWidgetIds) {
if (DEBUG) {
Log.d(TAG,
@@ -197,9 +201,8 @@ public class PeopleSpaceWidgetManager {
*/
private void updateStorageAndViewWithConversationData(ConversationChannel conversation,
int appWidgetId) {
- PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
- mContext,
- appWidgetId);
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(
+ mContext, appWidgetId, mAppWidgetManager, mIPeopleManager);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to");
return;
@@ -214,8 +217,7 @@ public class PeopleSpaceWidgetManager {
.setUserName(info.getLabel())
.setUserIcon(
PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable(
- info, 0))
- )
+ info, 0)))
.setContactUri(uri)
.setStatuses(conversation.getStatuses())
.setLastInteractionTimestamp(conversation.getLastEventTimestamp())
@@ -232,9 +234,8 @@ public class PeopleSpaceWidgetManager {
StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction,
int appWidgetId) {
- PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
- mContext,
- appWidgetId);
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(
+ mContext, appWidgetId, mAppWidgetManager, mIPeopleManager);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
@@ -248,9 +249,12 @@ public class PeopleSpaceWidgetManager {
}
storedTile = storedTile
.toBuilder()
+ // Reset notification content.
.setNotificationKey(null)
.setNotificationContent(null)
.setNotificationDataUri(null)
+ // Reset missed calls category.
+ .setNotificationCategory(null)
.build();
}
updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
@@ -312,18 +316,41 @@ public class PeopleSpaceWidgetManager {
}
};
- /** Adds {@code tile} mapped to {@code appWidgetId}. */
- public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) {
+ /**
+ * Checks if this widget has been added externally, and this the first time we are learning
+ * about the widget. If so, the widget adder should have populated options with PeopleTileKey
+ * arguments.
+ */
+ public void onAppWidgetOptionsChanged(int appWidgetId, Bundle newOptions) {
+ // Check if this widget has been added externally, and this the first time we are
+ // learning about the widget. If so, the widget adder should have populated options with
+ // PeopleTileKey arguments.
+ if (DEBUG) Log.d(TAG, "onAppWidgetOptionsChanged called for widget: " + appWidgetId);
+ PeopleTileKey optionsKey = AppWidgetOptionsHelper.getPeopleTileKeyFromBundle(newOptions);
+ if (optionsKey.isValid()) {
+ if (DEBUG) {
+ Log.d(TAG, "PeopleTileKey was present in Options, shortcutId: "
+ + optionsKey.getShortcutId());
+ }
+ addNewWidget(appWidgetId, optionsKey);
+ AppWidgetOptionsHelper.removePeopleTileKey(mAppWidgetManager, appWidgetId);
+ }
+ // Update views for new widget dimensions.
+ updateWidgets(new int[]{appWidgetId});
+ }
+
+ /** Adds{@code tile} mapped to {@code appWidgetId}. */
+ public void addNewWidget(int appWidgetId, PeopleTileKey key) {
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
synchronized (mLock) {
- if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getUserName());
- PeopleSpaceUtils.setStorageForTile(mContext, tile, appWidgetId);
+ if (DEBUG) Log.d(TAG, "Add storage for : " + key.getShortcutId());
+ PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId);
}
try {
- if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
- mLauncherApps.cacheShortcuts(tile.getPackageName(),
- Collections.singletonList(tile.getId()),
- tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+ if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + key.getShortcutId());
+ mLauncherApps.cacheShortcuts(key.getPackageName(),
+ Collections.singletonList(key.getShortcutId()),
+ UserHandle.of(key.getUserId()), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
Log.w(TAG, "Exception caching shortcut:" + e);
}
@@ -335,19 +362,16 @@ public class PeopleSpaceWidgetManager {
public void registerConversationListenerIfNeeded(int widgetId,
PeopleSpaceWidgetProvider.TileConversationListener newListener) {
// Retrieve storage needed for registration.
- String packageName;
- String shortcutId;
- int userId;
- String key;
+ PeopleTileKey key;
synchronized (mLock) {
SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
- packageName = widgetSp.getString(PACKAGE_NAME, null);
- shortcutId = widgetSp.getString(SHORTCUT_ID, null);
- userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
- key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
- if (key == null) {
- if (DEBUG) Log.e(TAG, "Could not register " + widgetId);
+ key = new PeopleTileKey(
+ widgetSp.getString(SHORTCUT_ID, EMPTY_STRING),
+ widgetSp.getInt(USER_ID, INVALID_USER_ID),
+ widgetSp.getString(PACKAGE_NAME, EMPTY_STRING));
+ if (!key.isValid()) {
+ if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId);
return;
}
}
@@ -359,9 +383,9 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key);
mListeners.put(key, newListener);
}
- mPeopleManager.registerConversationListener(packageName,
- userId,
- shortcutId, newListener,
+ mPeopleManager.registerConversationListener(key.getPackageName(),
+ key.getUserId(),
+ key.getShortcutId(), newListener,
mContext.getMainExecutor());
}
@@ -371,27 +395,24 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Widget removed: " + widgetId);
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
// Retrieve storage needed for widget deletion.
- String packageName;
- String shortcutId;
- int userId;
- String key;
+ PeopleTileKey key;
Set<String> storedWidgetIdsForKey;
synchronized (mLock) {
SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
- packageName = widgetSp.getString(PACKAGE_NAME, null);
- shortcutId = widgetSp.getString(SHORTCUT_ID, null);
- userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
- key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
- if (key == null) {
+ key = new PeopleTileKey(
+ widgetSp.getString(SHORTCUT_ID, null),
+ widgetSp.getInt(USER_ID, INVALID_USER_ID),
+ widgetSp.getString(PACKAGE_NAME, null));
+ if (!key.isValid()) {
if (DEBUG) Log.e(TAG, "Could not delete " + widgetId);
return;
}
storedWidgetIdsForKey = new HashSet<>(
- mSharedPrefs.getStringSet(key, new HashSet<>()));
+ mSharedPrefs.getStringSet(key.toString(), new HashSet<>()));
}
synchronized (mLock) {
- PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId);
+ PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId);
}
// If another tile with the conversation is still stored, we need to keep the listener.
if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString());
@@ -399,13 +420,13 @@ public class PeopleSpaceWidgetManager {
&& storedWidgetIdsForKey.size() == 1) {
if (DEBUG) Log.d(TAG, "Remove caching and listener");
unregisterConversationListener(key, widgetId);
- uncacheConversationShortcut(shortcutId, packageName, userId);
+ uncacheConversationShortcut(key);
}
}
}
/** Unregisters the conversation listener for {@code appWidgetId}. */
- private void unregisterConversationListener(String key, int appWidgetId) {
+ private void unregisterConversationListener(PeopleTileKey key, int appWidgetId) {
PeopleSpaceWidgetProvider.TileConversationListener registeredListener;
synchronized (mListeners) {
registeredListener = mListeners.get(key);
@@ -420,12 +441,12 @@ public class PeopleSpaceWidgetManager {
}
/** Uncaches the conversation shortcut. */
- private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) {
+ private void uncacheConversationShortcut(PeopleTileKey key) {
try {
- if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
- mLauncherApps.uncacheShortcuts(packageName,
- Collections.singletonList(shortcutId),
- UserHandle.of(userId),
+ if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + key.getShortcutId());
+ mLauncherApps.uncacheShortcuts(key.getPackageName(),
+ Collections.singletonList(key.getShortcutId()),
+ UserHandle.of(key.getUserId()),
LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
Log.d(TAG, "Exception uncaching shortcut:" + e);
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index cccf7aa13028..3bc5b29bd05d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -22,6 +22,7 @@ import android.app.people.PeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
+import android.os.Bundle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,18 +68,21 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
ensurePeopleSpaceWidgetManagerInitialized(context);
peopleSpaceWidgetManager.updateWidgets(appWidgetIds);
for (int appWidgetId : appWidgetIds) {
+ if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId);
PeopleSpaceWidgetProvider.TileConversationListener
newListener = new PeopleSpaceWidgetProvider.TileConversationListener();
peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId,
newListener);
}
- return;
}
- private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
- if (peopleSpaceWidgetManager == null) {
- peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
- }
+ /** Called when widget updates. */
+ @Override
+ public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
+ int appWidgetId, Bundle newOptions) {
+ super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
+ ensurePeopleSpaceWidgetManagerInitialized(context);
+ peopleSpaceWidgetManager.onAppWidgetOptionsChanged(appWidgetId, newOptions);
}
@Override
@@ -88,6 +92,12 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
peopleSpaceWidgetManager.deleteWidgets(appWidgetIds);
}
+ private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
+ if (peopleSpaceWidgetManager == null) {
+ peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
+ }
+ }
+
@VisibleForTesting
public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) {
peopleSpaceWidgetManager = manager;
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index 050352292b38..87b2a15d1c55 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -106,7 +106,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
PeopleSpaceTile tile = mTiles.get(i);
String status = PeopleSpaceUtils.getLastInteractionString(mContext,
- tile.getLastInteractionTimestamp(), true);
+ tile.getLastInteractionTimestamp());
personView.setTextViewText(R.id.status, status);
personView.setTextViewText(R.id.name, tile.getUserName().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java
new file mode 100644
index 000000000000..ac42cb08090a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.people.widget;
+
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
+
+import android.text.TextUtils;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.Objects;
+
+/** Class that encapsulates fields identifying a Conversation. */
+public class PeopleTileKey {
+ private String mShortcutId;
+ private int mUserId;
+ private String mPackageName;
+
+ public PeopleTileKey(String shortcutId, int userId, String packageName) {
+ mShortcutId = shortcutId;
+ mUserId = userId;
+ mPackageName = packageName;
+ }
+
+ public PeopleTileKey(NotificationEntry entry) {
+ mShortcutId = entry.getRanking() != null
+ && entry.getRanking().getConversationShortcutInfo() != null
+ ? entry.getRanking().getConversationShortcutInfo().getId()
+ : EMPTY_STRING;
+ mUserId = entry.getSbn().getUser() != null
+ ? entry.getSbn().getUser().getIdentifier() : INVALID_USER_ID;
+ mPackageName = entry.getSbn().getPackageName();
+ }
+
+ public String getShortcutId() {
+ return mShortcutId;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** Returns whether PeopleTileKey is valid/well-formed. */
+ public boolean isValid() {
+ return !TextUtils.isEmpty(mShortcutId) && !TextUtils.isEmpty(mPackageName) && mUserId >= 0;
+ }
+
+ /**
+ * Returns the uniquely identifying key for the conversation.
+ *
+ * <p>{@code userId} will always be a number, so we put user ID as the
+ * delimiter between the app-provided strings of shortcut ID and package name.
+ *
+ * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+ * a {@code packageName} to always start with a letter. This restriction means we are
+ * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+ * case is impossible given the package name restrictions:
+ * <ul>
+ * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+ * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+ * </ul>
+ */
+ @Override
+ public String toString() {
+ if (!isValid()) return null;
+ return mShortcutId + "/" + mUserId + "/" + mPackageName;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof PeopleTileKey)) {
+ return false;
+ }
+ final PeopleTileKey o = (PeopleTileKey) other;
+ return Objects.equals(o.toString(), this.toString());
+ }
+
+ @Override
+ public int hashCode() {
+ return mPackageName.hashCode() + Integer.valueOf(mUserId).hashCode()
+ + mShortcutId.hashCode();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index 03c184336364..f87ea7c61ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -214,9 +214,7 @@ class PrivacyDialogController(
private fun filterType(type: PrivacyType?): PrivacyType? {
return type?.let {
- if (privacyItemController.allIndicatorsAvailable) {
- it
- } else if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
+ if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
privacyItemController.micCameraAvailable) {
it
} else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 1e0451601e50..03d6154706eb 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -68,8 +68,6 @@ class PrivacyItemController @Inject constructor(
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
}
const val TAG = "PrivacyItemController"
- private const val ALL_INDICATORS =
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
private const val DEFAULT_ALL_INDICATORS = false
@@ -83,11 +81,6 @@ class PrivacyItemController @Inject constructor(
@Synchronized get() = field.toList() // Returns a shallow copy of the list
@Synchronized set
- private fun isAllIndicatorsEnabled(): Boolean {
- return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
- }
-
private fun isMicCameraEnabled(): Boolean {
return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
MIC_CAMERA, DEFAULT_MIC_CAMERA)
@@ -120,34 +113,29 @@ class PrivacyItemController @Inject constructor(
uiExecutor.execute(notifyChanges)
}
- var allIndicatorsAvailable = isAllIndicatorsEnabled()
- private set
var micCameraAvailable = isMicCameraEnabled()
private set
var locationAvailable = isLocationEnabled()
+ var allIndicatorsAvailable = micCameraAvailable && locationAvailable
+
private val devicePropertiesChangedListener =
object : DeviceConfig.OnPropertiesChangedListener {
override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
- (properties.keyset.contains(ALL_INDICATORS) ||
- properties.keyset.contains(MIC_CAMERA) ||
+ (properties.keyset.contains(MIC_CAMERA) ||
properties.keyset.contains(LOCATION))) {
// Running on the ui executor so can iterate on callbacks
- if (properties.keyset.contains(ALL_INDICATORS)) {
- allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS,
- DEFAULT_ALL_INDICATORS)
- callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
- }
-
if (properties.keyset.contains(MIC_CAMERA)) {
micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
+ allIndicatorsAvailable = micCameraAvailable && locationAvailable
callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
}
if (properties.keyset.contains(LOCATION)) {
locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+ allIndicatorsAvailable = micCameraAvailable && locationAvailable
callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
}
internalUiExecutor.updateListeningState()
@@ -163,8 +151,7 @@ class PrivacyItemController @Inject constructor(
active: Boolean
) {
// Check if we care about this code right now
- if (!allIndicatorsAvailable &&
- (code in OPS_LOCATION && !locationAvailable)) {
+ if (code in OPS_LOCATION && !locationAvailable) {
return
}
val userId = UserHandle.getUserId(uid)
@@ -231,7 +218,7 @@ class PrivacyItemController @Inject constructor(
*/
private fun setListeningState() {
val listen = !callbacks.isEmpty() and
- (allIndicatorsAvailable || micCameraAvailable || locationAvailable)
+ (micCameraAvailable || locationAvailable)
if (listening == listen) return
listening = listen
if (listening) {
@@ -338,7 +325,7 @@ class PrivacyItemController @Inject constructor(
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}
- if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) {
+ if (type == PrivacyType.TYPE_LOCATION && !locationAvailable) {
return null
}
val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 0fa7b59d0e54..5ab7bd88e49b 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -88,7 +88,6 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
private boolean mViewAndWindowAdded;
private ObjectAnimator mAnimator;
- private boolean mAllIndicatorsFlagEnabled;
private boolean mMicCameraIndicatorFlagEnabled;
private boolean mLocationIndicatorEnabled;
private List<PrivacyItem> mPrivacyItems;
@@ -111,12 +110,10 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin));
mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
- mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
mLocationIndicatorEnabled = privacyItemController.getLocationAvailable();
if (DEBUG) {
- Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled);
Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled);
Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled);
}
@@ -135,12 +132,6 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
}
@Override
- public void onFlagAllChanged(boolean flag) {
- if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag);
- mAllIndicatorsFlagEnabled = flag;
- }
-
- @Override
public void onFlagMicCameraChanged(boolean flag) {
if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag);
mMicCameraIndicatorFlagEnabled = flag;
@@ -155,8 +146,8 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
private void updateUI() {
if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
- if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled
- || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) {
+ if ((mMicCameraIndicatorFlagEnabled || mLocationIndicatorEnabled)
+ && !mPrivacyItems.isEmpty()) {
if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) {
showIndicator();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index fe76668ab68b..b8823e148e68 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -35,6 +37,7 @@ import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -77,6 +80,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// This animates fading of SecurityFooter and media divider
private TouchAnimator mAllPagesDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
+ private HeightExpansionAnimator mQQSTileHeightAnimator;
+ private HeightExpansionAnimator mOtherTilesExpandAnimator;
+
private boolean mNeedsAnimatorUpdate = false;
private boolean mOnKeyguard;
@@ -161,7 +167,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
@Override
public void onViewAttachedToWindow(View v) {
mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
- MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
+ MOVE_FULL_ROWS);
}
@Override
@@ -179,9 +185,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
} else if (MOVE_FULL_ROWS.equals(key)) {
mFullRows = TunerService.parseIntegerSwitch(newValue, true);
- } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
- mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue);
- clearAnimationState();
}
updateAnimators();
}
@@ -209,6 +212,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
clearAnimationState();
mAllViews.clear();
mQuickQsViews.clear();
+ mQQSTileHeightAnimator = null;
+ mOtherTilesExpandAnimator = null;
+
+ mNumQuickTiles = mQuickQsPanel.getNumQuickTiles();
QSTileLayout tileLayout = mQsPanelController.getTileLayout();
mAllViews.add((View) tileLayout);
@@ -218,6 +225,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
+ mQs.getHeader().getPaddingBottom();
firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
+ boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled();
+ int qqsTileHeight = 0;
+
for (QSTile tile : tiles) {
QSTileView tileView = mQsPanelController.getTileView(tile);
if (tileView == null) {
@@ -237,22 +247,47 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
getRelativePosition(loc2, tileIcon, view);
final int xDiff = loc2[0] - loc1[0];
- final int yDiff = loc2[1] - loc1[1];
-
+ int yDiff = loc2[1] - loc1[1];
if (count < tileLayout.getNumVisibleTiles()) {
+ getRelativePosition(loc1, quickTileView, view);
+ getRelativePosition(loc2, tileView, view);
+ int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
// Move the quick tile right from its location to the new one.
- translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
-
- // Counteract the parent translation on the tile. So we have a static base to
- // animate the label position off from.
- //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+ View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
+ translationXBuilder.addFloat(v, "translationX", 0, xDiff);
+ translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
+ mAllViews.add(v);
// Move the real tile from the quick tile position to its final
// location.
- translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
- translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+ v = qsSideLabelsEnabled ? tileIcon : tileView;
+ translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
+ translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
+
+ if (qsSideLabelsEnabled) {
+ translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
+ translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+
+ if (mQQSTileHeightAnimator == null) {
+ mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+ quickTileView.getHeight(), tileView.getHeight());
+ qqsTileHeight = quickTileView.getHeight();
+ }
+
+ mQQSTileHeightAnimator.addView(quickTileView);
+ View qqsLabelContainer = quickTileView.getLabelContainer();
+ View qsLabelContainer = tileView.getLabelContainer();
+
+ getRelativePosition(loc1, qqsLabelContainer, view);
+ getRelativePosition(loc2, qsLabelContainer, view);
+ yDiff = loc2[1] - loc1[1] - yOffset;
+
+ translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
+ translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
+ mAllViews.add(qqsLabelContainer);
+ mAllViews.add(qsLabelContainer);
+ }
} else { // These tiles disappear when expanding
firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
@@ -265,7 +300,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationX);
}
- if (mFeatureFlags.isQSLabelsEnabled()) {
+ if (qsSideLabelsEnabled) {
mQuickQsViews.add(tileView);
} else {
mQuickQsViews.add(tileView.getIconWithBackground());
@@ -278,9 +313,29 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mAllViews.add(tileIcon);
} else {
- firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
- firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+ if (!qsSideLabelsEnabled) {
+ firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
+ firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+ } else {
+ // Pretend there's a corresponding QQS tile (for the position) that we are
+ // expanding from.
+ SideLabelTileLayout qqsLayout =
+ (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
+ getRelativePosition(loc1, qqsLayout, view);
+ getRelativePosition(loc2, tileView, view);
+ int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
+ translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
+ if (mOtherTilesExpandAnimator == null) {
+ mOtherTilesExpandAnimator =
+ new HeightExpansionAnimator(
+ this, qqsTileHeight, tileView.getHeight());
+ }
+ mOtherTilesExpandAnimator.addView(tileView);
+ tileView.setClipChildren(true);
+ tileView.setClipToPadding(true);
+ }
}
+
mAllViews.add(tileView);
count++;
}
@@ -303,7 +358,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
.build();
// Fade in the tiles/labels as we reach the final position.
Builder builder = new Builder()
- .setStartDelay(EXPANDED_TILE_DELAY)
+ .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY)
.addFloat(tileLayout, "alpha", 0, 1);
mFirstPageDelayedAnimator = builder.build();
@@ -331,6 +386,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
mTranslationXAnimator = translationXBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ }
+ if (mOtherTilesExpandAnimator != null) {
+ mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ }
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -404,6 +465,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if (mBrightnessAnimator != null) {
mBrightnessAnimator.setPosition(position);
}
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setPosition(position);
+ }
+ if (mOtherTilesExpandAnimator != null) {
+ mOtherTilesExpandAnimator.setPosition(position);
+ }
} else {
mNonfirstPageAnimator.setPosition(position);
mNonfirstPageDelayedAnimator.setPosition(position);
@@ -446,6 +513,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
v.setAlpha(1);
v.setTranslationX(0);
v.setTranslationY(0);
+ if (v instanceof SideLabelTileLayout) {
+ ((SideLabelTileLayout) v).setClipChildren(false);
+ ((SideLabelTileLayout) v).setClipToPadding(false);
+ }
+ }
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.resetViewsHeights();
+ }
+ if (mOtherTilesExpandAnimator != null) {
+ mOtherTilesExpandAnimator.resetViewsHeights();
}
final int N2 = mQuickQsViews.size();
for (int i = 0; i < N2; i++) {
@@ -483,4 +560,61 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
updateAnimators();
setCurrentPosition();
};
+
+ static class HeightExpansionAnimator {
+ private final List<View> mViews = new ArrayList<>();
+ private final ValueAnimator mAnimator;
+ private final TouchAnimator.Listener mListener;
+
+ private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ float mLastT = -1;
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float t = valueAnimator.getAnimatedFraction();
+ if (t == 0f) {
+ mListener.onAnimationAtStart();
+ } else if (t == 1f) {
+ mListener.onAnimationAtEnd();
+ } else if (mLastT <= 0 || mLastT == 1) {
+ mListener.onAnimationStarted();
+ }
+ mLastT = t;
+ final int viewCount = mViews.size();
+ int height = (Integer) valueAnimator.getAnimatedValue();
+ for (int i = 0; i < viewCount; i++) {
+ View v = mViews.get(i);
+ v.setBottom(v.getTop() + height);
+ }
+ }
+ };
+
+ HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) {
+ mListener = listener;
+ mAnimator = ValueAnimator.ofInt(startHeight, endHeight);
+ mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mAnimator.addUpdateListener(mUpdateListener);
+ }
+
+ void addView(View v) {
+ mViews.add(v);
+ }
+
+ void setInterpolator(TimeInterpolator interpolator) {
+ mAnimator.setInterpolator(interpolator);
+ }
+
+ void setPosition(float position) {
+ mAnimator.setCurrentFraction(position);
+ }
+
+ void resetViewsHeights() {
+ final int viewsCount = mViews.size();
+ for (int i = 0; i < viewsCount; i++) {
+ View v = mViews.get(i);
+ v.setBottom(v.getTop() + v.getMeasuredHeight());
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 7e20be6826dc..3d8784b29e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -94,7 +94,7 @@ public class QSFooterView extends FrameLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mEdit = findViewById(android.R.id.edit);
+ mEdit = requireViewById(android.R.id.edit);
mPageIndicator = findViewById(R.id.footer_page_indicator);
@@ -104,14 +104,15 @@ public class QSFooterView extends FrameLayout {
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
- mActionsContainer = findViewById(R.id.qs_footer_actions_container);
+ mActionsContainer = requireViewById(R.id.qs_footer_actions_container);
mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
mBuildText = findViewById(R.id.build);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
- ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
-
+ if (mSettingsButton.getBackground() instanceof RippleDrawable) {
+ ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+ }
updateResources();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -143,7 +144,7 @@ public class QSFooterView extends FrameLayout {
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
mSettingsCogAnimator = new Builder()
- .addFloat(mSettingsContainer, "translationX",
+ .addFloat(mSettingsButton, "translationX",
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
.addFloat(mSettingsButton, "rotation", -120, 0)
.build();
@@ -173,12 +174,15 @@ public class QSFooterView extends FrameLayout {
@Nullable
private TouchAnimator createFooterAnimator() {
- return new TouchAnimator.Builder()
+ TouchAnimator.Builder builder = new TouchAnimator.Builder()
.addFloat(mActionsContainer, "alpha", 0, 1)
- .addFloat(mEditContainer, "alpha", 0, 1)
.addFloat(mPageIndicator, "alpha", 0, 1)
- .setStartDelay(0.9f)
- .build();
+ .addFloat(mBuildText, "alpha", 0, 1)
+ .setStartDelay(0.9f);
+ if (mEditContainer != null) {
+ builder.addFloat(mEditContainer, "alpha", 0, 1);
+ }
+ return builder.build();
}
/** */
@@ -273,8 +277,10 @@ public class QSFooterView extends FrameLayout {
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
- mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
- mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+ mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.GONE);
+ if (mEditContainer != null) {
+ mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+ }
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 2bea72cc0c7e..f1f4e16206a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -153,8 +153,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
@Override
protected void onViewAttached() {
- if (mShowPMLiteButton) {
- mPowerMenuLite.setVisibility(View.VISIBLE);
+ if (!mShowPMLiteButton) {
+ mPowerMenuLite.setVisibility(View.GONE);
}
mView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ed308ae1ac6c..7c9f0b082d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -430,6 +430,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion);
+ mQuickQSPanelController.getTileLayout().setExpansion(expansion);
mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
if (fullyCollapsed) {
mQSPanelScrollView.setScrollY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7657dcead583..c794a2121cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -32,6 +32,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStub;
import android.widget.LinearLayout;
import com.android.internal.logging.UiEventLogger;
@@ -127,8 +128,21 @@ public class QSPanel extends LinearLayout implements Tunable {
}
+ protected void inflateQSFooter(boolean newFooter) {
+ ViewStub stub = findViewById(R.id.qs_footer_stub);
+ if (stub != null) {
+ stub.setLayoutResource(
+ newFooter ? R.layout.qs_footer_impl_two_lines : R.layout.qs_footer_impl);
+ stub.inflate();
+ mFooter = findViewById(R.id.qs_footer);
+ }
+ }
+
void initialize(boolean sideLabels) {
mSideLabels = sideLabels;
+
+ inflateQSFooter(sideLabels);
+
mRegularTileLayout = createRegularTileLayout();
mTileLayout = mRegularTileLayout;
@@ -301,7 +315,7 @@ public class QSPanel extends LinearLayout implements Tunable {
int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
R.dimen.qs_footer_horizontal_margin);
- mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
+ mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f);
updatePadding();
updatePageIndicator();
@@ -344,7 +358,6 @@ public class QSPanel extends LinearLayout implements Tunable {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mFooter = findViewById(R.id.qs_footer);
mDivider = findViewById(R.id.divider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 1411fa1ea21f..5e13fd345b81 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.qs;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW;
import android.app.AlertDialog;
@@ -244,8 +246,14 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
if (organizationName == null) {
return mContext.getString(R.string.quick_settings_disclosure_management);
}
- return mContext.getString(R.string.quick_settings_disclosure_named_management,
- organizationName);
+ if (isFinancedDevice()) {
+ return mContext.getString(
+ R.string.quick_settings_financed_disclosure_named_management,
+ organizationName);
+ } else {
+ return mContext.getString(R.string.quick_settings_disclosure_named_management,
+ organizationName);
+ }
} // end if(isDeviceManaged)
if (hasCACertsInWorkProfile) {
if (workProfileOrganizationName == null) {
@@ -355,6 +363,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
.inflate(R.layout.quick_settings_footer_dialog, null, false);
// device management section
+ TextView deviceManagementSubtitle =
+ dialogView.findViewById(R.id.device_management_subtitle);
+ deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization));
+
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
workProfileOrganizationName);
@@ -468,7 +480,8 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
}
- private String getSettingsButton() {
+ @VisibleForTesting
+ String getSettingsButton() {
return mContext.getString(R.string.monitoring_button_view_policies);
}
@@ -490,8 +503,13 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return null;
}
if (isDeviceManaged && organizationName != null) {
- return mContext.getString(
- R.string.monitoring_description_named_management, organizationName);
+ if (isFinancedDevice()) {
+ return mContext.getString(R.string.monitoring_financed_description_named_management,
+ organizationName, organizationName);
+ } else {
+ return mContext.getString(
+ R.string.monitoring_description_named_management, organizationName);
+ }
} else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
return mContext.getString(
R.string.monitoring_description_named_management, workProfileOrganizationName);
@@ -557,14 +575,23 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return message;
}
- private int getTitle(String deviceOwner) {
- if (deviceOwner != null) {
- return R.string.monitoring_title_device_owned;
+ @VisibleForTesting
+ CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) {
+ if (deviceOwnerOrganization != null && isFinancedDevice()) {
+ return mContext.getString(R.string.monitoring_title_financed_device,
+ deviceOwnerOrganization);
} else {
- return R.string.monitoring_title;
+ return mContext.getString(R.string.monitoring_title_device_owned);
}
}
+ private boolean isFinancedDevice() {
+ return mSecurityController.isDeviceManaged()
+ && mSecurityController.getDeviceOwnerType(
+ mSecurityController.getDeviceOwnerComponentOnAnyUser())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ }
+
private final Runnable mUpdateIcon = new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 2cd367d03102..3e3451e64b49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -445,11 +445,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
final ArrayList<String> tiles = new ArrayList<String>();
boolean addedDefault = false;
Set<String> addedSpecs = new ArraySet<>();
- // TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- tiles.add("internet");
- addedSpecs.add("internet");
- }
for (String tile : tileList.split(",")) {
tile = tile.trim();
if (tile.isEmpty()) continue;
@@ -457,17 +452,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (!addedDefault) {
List<String> defaultSpecs = getDefaultSpecs(context);
for (String spec : defaultSpecs) {
- // TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(
- context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (spec.equals("wifi") || spec.equals("cell")) {
- continue;
- }
- } else {
- if (spec.equals("internet")) {
- continue;
- }
- }
if (!addedSpecs.contains(spec)) {
tiles.add(spec);
addedSpecs.add(spec);
@@ -476,18 +460,40 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
addedDefault = true;
}
} else {
- // TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (tile.equals("wifi") || tile.equals("cell")) {
- continue;
- }
- }
if (!addedSpecs.contains(tile)) {
tiles.add(tile);
addedSpecs.add(tile);
}
}
}
+ // TODO(b/174753536): Move it into the config file.
+ // Only do the below hacking when at least one of the below tiles exist
+ // --InternetTile
+ // --WiFiTile
+ // --CellularTIle
+ if (tiles.contains("internet") || tiles.contains("wifi") || tiles.contains("cell")) {
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ if (!tiles.contains("internet")) {
+ tiles.add("internet");
+ }
+ if (tiles.contains("wifi")) {
+ tiles.remove("wifi");
+ }
+ if (tiles.contains("cell")) {
+ tiles.remove("cell");
+ }
+ } else {
+ if (tiles.contains("internet")) {
+ tiles.remove("internet");
+ }
+ if (!tiles.contains("wifi")) {
+ tiles.add("wifi");
+ }
+ if (!tiles.contains("cell")) {
+ tiles.add("cell");
+ }
+ }
+ }
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 9b66b59c06df..e7828c366b64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -55,6 +55,11 @@ public class QuickQSPanel extends QSPanel {
applyBottomMargin((View) mRegularTileLayout);
}
+ @Override
+ protected void inflateQSFooter(boolean newFooter) {
+ // No footer
+ }
+
private void applyBottomMargin(View view) {
int margin = getResources().getDimensionPixelSize(R.dimen.qs_header_tile_margin_bottom);
MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
@@ -343,19 +348,20 @@ public class QuickQSPanel extends QSPanel {
}
static class QQSSideLabelTileLayout extends SideLabelTileLayout {
+
QQSSideLabelTileLayout(Context context) {
super(context, null);
setClipChildren(false);
setClipToPadding(false);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_HORIZONTAL;
setLayoutParams(lp);
setMaxColumns(4);
}
@Override
public boolean updateResources() {
+ mCellHeightResId = R.dimen.qs_quick_tile_size;
boolean b = super.updateResources();
mMaxAllowedRows = 2;
return b;
@@ -374,5 +380,38 @@ public class QuickQSPanel extends QSPanel {
updateMaxRows(10000, mRecords.size());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ @Override
+ public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+ boolean startedListening = !mListening && listening;
+ super.setListening(listening, uiEventLogger);
+ if (startedListening) {
+ // getNumVisibleTiles() <= mRecords.size()
+ for (int i = 0; i < getNumVisibleTiles(); i++) {
+ QSTile tile = mRecords.get(i).tile;
+ uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
+ tile.getMetricsSpec(), tile.getInstanceId());
+ }
+ }
+ }
+
+ @Override
+ public void setExpansion(float expansion) {
+ if (expansion > 0f && expansion < 1f) {
+ return;
+ }
+ boolean selected = expansion == 0f;
+ // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
+ // point we want them to be selected so the tiles will marquee (but not at other points
+ // of expansion.
+ // We set it as not important while we change this, so setting each tile as selected
+ // will not cause them to announce themselves until the user has actually selected the
+ // item.
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).setSelected(selected);
+ }
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 671f8f7dd2d1..fee56b984ecc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -99,7 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
break;
}
}
- super.setTiles(tiles, !mQSLabelFlag);
+ super.setTiles(tiles, /* collapsedView */ true);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index e3c39aa77402..eedcdab68b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -97,7 +97,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private boolean mListening;
private AlarmClockInfo mNextAlarm;
- private boolean mAllIndicatorsEnabled;
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
@@ -151,14 +150,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
@Override
- public void onFlagAllChanged(boolean flag) {
- if (mAllIndicatorsEnabled != flag) {
- mAllIndicatorsEnabled = flag;
- update();
- }
- }
-
- @Override
public void onFlagMicCameraChanged(boolean flag) {
if (mMicCameraIndicatorsEnabled != flag) {
mMicCameraIndicatorsEnabled = flag;
@@ -270,7 +261,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mRingerContainer.setOnClickListener(mOnClickListener);
mPrivacyChip.setOnClickListener(mOnClickListener);
- mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
@@ -321,7 +311,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mNextAlarmController.addCallback(mNextAlarmChangeCallback);
mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
// Get the most up to date info
- mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
mPrivacyItemController.addCallback(mPICCallback);
@@ -353,13 +342,13 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private List<String> getIgnoredIconSlots() {
ArrayList<String> ignored = new ArrayList<>();
if (getChipEnabled()) {
- if (mAllIndicatorsEnabled || mMicCameraIndicatorsEnabled) {
+ if (mMicCameraIndicatorsEnabled) {
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_camera));
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_microphone));
}
- if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
+ if (mLocationIndicatorsEnabled) {
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_location));
}
@@ -368,7 +357,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
private boolean getChipEnabled() {
- return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled;
+ return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
private boolean isZenOverridingRinger() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 52f111e7ab48..0ebadfd2fa11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -33,4 +33,15 @@ open class SideLabelTileLayout(
override fun isFull(): Boolean {
return mRecords.size >= maxTiles()
}
+
+ /**
+ * Return the position from the top of the layout of the tile with this index.
+ *
+ * This will return a position even for indices that go beyond [maxTiles], continuing the rows
+ * beyond that.
+ */
+ fun getPhantomTopPosition(index: Int): Int {
+ val row = index / mColumns
+ return getRowTop(row)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c1ce4a577dda..9e0aa5ad78b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -25,6 +25,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
protected int mColumns;
protected int mCellWidth;
+ protected int mCellHeightResId = R.dimen.qs_tile_height;
protected int mCellHeight;
protected int mMaxCellHeight;
protected int mCellMarginHorizontal;
@@ -118,7 +119,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
final Resources res = mContext.getResources();
mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
updateColumns();
- mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+ mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
@@ -235,7 +236,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
layoutTileRecords(mRecords.size());
}
- private int getRowTop(int row) {
+ protected int getRowTop(int row) {
return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
index f8c0dd4239d9..7977b4904a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
@@ -14,7 +14,7 @@ import com.android.systemui.qs.tileimpl.QSTileViewHorizontal
class CustomizeTileViewHorizontal(
context: Context,
icon: QSIconView
-) : QSTileViewHorizontal(context, icon),
+) : QSTileViewHorizontal(context, icon, collapsed = false),
TileAdapter.CustomizeView {
private var showAppLabel = false
@@ -27,6 +27,7 @@ class CustomizeTileViewHorizontal(
override fun handleStateChanged(state: QSTile.State) {
super.handleStateChanged(state)
+ mShowRippleEffect = false
mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 7eeb4bd19f1a..32b41ec0ab66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -115,9 +115,32 @@ public class TileQueryHelper {
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
// TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (!possibleTiles.contains("internet")) {
- possibleTiles.add("internet");
+ // Only do the below hacking when at least one of the below tiles exist
+ // --InternetTile
+ // --WiFiTile
+ // --CellularTIle
+ if (possibleTiles.contains("internet") || possibleTiles.contains("wifi")
+ || possibleTiles.contains("cell")) {
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ if (!possibleTiles.contains("internet")) {
+ possibleTiles.add("internet");
+ }
+ if (possibleTiles.contains("wifi")) {
+ possibleTiles.remove("wifi");
+ }
+ if (possibleTiles.contains("cell")) {
+ possibleTiles.remove("cell");
+ }
+ } else {
+ if (possibleTiles.contains("internet")) {
+ possibleTiles.remove("internet");
+ }
+ if (!possibleTiles.contains("wifi")) {
+ possibleTiles.add("wifi");
+ }
+ if (!possibleTiles.contains("cell")) {
+ possibleTiles.add("cell");
+ }
}
}
for (String spec : possibleTiles) {
@@ -125,15 +148,6 @@ public class TileQueryHelper {
// Do not include CustomTile. Those will be created by `addPackageTiles`.
if (spec.startsWith(CustomTile.PREFIX)) continue;
// TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (spec.equals("wifi") || spec.equals("cell")) {
- continue;
- }
- } else {
- if (spec.equals("internet")) {
- continue;
- }
- }
final QSTile tile = host.createTile(spec);
if (tile == null) {
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 56d06eb1b474..1699a34e6c08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -51,6 +51,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -95,6 +96,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
QSHost host,
Looper backgroundLooper,
Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -102,8 +104,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
String action,
Context userContext
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
mTile = new Tile();
@@ -450,6 +452,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
final Lazy<QSHost> mQSHostLazy;
final Looper mBackgroundLooper;
final Handler mMainHandler;
+ private final FalsingManager mFalsingManager;
final MetricsLogger mMetricsLogger;
final StatusBarStateController mStatusBarStateController;
final ActivityStarter mActivityStarter;
@@ -463,6 +466,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
Lazy<QSHost> hostLazy,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -471,6 +475,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mQSHostLazy = hostLazy;
mBackgroundLooper = backgroundLooper;
mMainHandler = mainHandler;
+ mFalsingManager = falsingManager;
mMetricsLogger = metricsLogger;
mStatusBarStateController = statusBarStateController;
mActivityStarter = activityStarter;
@@ -496,6 +501,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mQSHostLazy.get(),
mBackgroundLooper,
mMainHandler,
+ mFalsingManager,
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 6983b38489f6..ba349c6273d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -30,6 +30,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
+import com.android.systemui.qs.tiles.AlarmTile;
import com.android.systemui.qs.tiles.BatterySaverTile;
import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CameraToggleTile;
@@ -46,6 +47,7 @@ import com.android.systemui.qs.tiles.LocationTile;
import com.android.systemui.qs.tiles.MicrophoneToggleTile;
import com.android.systemui.qs.tiles.NfcTile;
import com.android.systemui.qs.tiles.NightDisplayTile;
+import com.android.systemui.qs.tiles.QuickAccessWalletTile;
import com.android.systemui.qs.tiles.ReduceBrightColorsTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.ScreenRecordTile;
@@ -91,6 +93,8 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<CameraToggleTile> mCameraToggleTileProvider;
private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
+ private final Provider<AlarmTile> mAlarmTileProvider;
+ private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -126,7 +130,9 @@ public class QSFactoryImpl implements QSFactory {
Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider,
Provider<CameraToggleTile> cameraToggleTileProvider,
Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
- Provider<DeviceControlsTile> deviceControlsTileProvider) {
+ Provider<DeviceControlsTile> deviceControlsTileProvider,
+ Provider<AlarmTile> alarmTileProvider,
+ Provider<QuickAccessWalletTile> quickAccessWalletTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -157,6 +163,8 @@ public class QSFactoryImpl implements QSFactory {
mCameraToggleTileProvider = cameraToggleTileProvider;
mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
mDeviceControlsTileProvider = deviceControlsTileProvider;
+ mAlarmTileProvider = alarmTileProvider;
+ mQuickAccessWalletTileProvider = quickAccessWalletTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -218,6 +226,10 @@ public class QSFactoryImpl implements QSFactory {
return mMicrophoneToggleTileProvider.get();
case "controls":
return mDeviceControlsTileProvider.get();
+ case "alarm":
+ return mAlarmTileProvider.get();
+ case "wallet":
+ return mQuickAccessWalletTileProvider.get();
}
// Custom tiles
@@ -242,10 +254,10 @@ public class QSFactoryImpl implements QSFactory {
public QSTileView createTileView(QSTile tile, boolean collapsedView) {
Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
QSIconView icon = tile.createTileView(context);
- if (collapsedView) {
+ if (mSideLabels) {
+ return new QSTileViewHorizontal(context, icon, collapsedView);
+ } else if (collapsedView) {
return new QSTileBaseView(context, icon, collapsedView);
- } else if (mSideLabels) {
- return new QSTileViewHorizontal(context, icon);
} else {
return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
}
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 33ca7d6bafd8..a45b131902c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -58,13 +58,13 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
protected final Handler mHandler = new H();
private final int[] mLocInScreen = new int[2];
- private final FrameLayout mIconFrame;
+ protected final FrameLayout mIconFrame;
protected QSIconView mIcon;
protected RippleDrawable mRipple;
protected Drawable mTileBackground;
private String mAccessibilityClass;
private boolean mTileState;
- private boolean mCollapsedView;
+ protected boolean mCollapsedView;
protected boolean mShowRippleEffect = true;
private float mStrokeWidthActive;
private float mStrokeWidthInactive;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 5a81676567a7..f9d1def3b716 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -58,6 +58,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -103,6 +104,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private final StatusBarStateController mStatusBarStateController;
protected final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
+ private final FalsingManager mFalsingManager;
private final QSLogger mQSLogger;
private volatile int mReadyState;
@@ -159,6 +161,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
QSHost host,
Looper backgroundLooper,
Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -171,6 +174,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mUiHandler = mainHandler;
mHandler = new H(backgroundLooper);
+ mFalsingManager = falsingManager;
mQSLogger = qsLogger;
mMetricsLogger = metricsLogger;
mStatusBarStateController = statusBarStateController;
@@ -608,7 +612,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mContext, mEnforcedAdmin);
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
} else {
- handleClick();
+ if (!mFalsingManager.isFalseTap(true, 0.1)) {
+ handleClick();
+ }
}
} else if (msg.what == SECONDARY_CLICK) {
name = "handleSecondaryClick";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index b59326ae56d5..c7ed89ba49b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -26,6 +26,8 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.Utils;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -116,7 +118,8 @@ public class QSTileView extends QSTileBaseView {
}
}
- private boolean shouldLabelBeSingleLine() {
+ protected boolean shouldLabelBeSingleLine() {
+ if (mCollapsedView) return true;
if (mLabel.getLineCount() > mMaxLabelLines) {
return true;
} else if (!TextUtils.isEmpty(mSecondLine.getText())
@@ -138,14 +141,14 @@ public class QSTileView extends QSTileBaseView {
} else {
labelColor = mColorLabelUnavailable;
}
- mLabel.setTextColor(labelColor);
+ changeLabelColor(labelColor);
mState = state.state;
mLabel.setText(state.label);
}
if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
mSecondLine.setText(state.secondaryLabel);
- mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
- : View.VISIBLE);
+ mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView
+ ? View.GONE : View.VISIBLE);
}
boolean dualTarget = mDualTargetAllowed && state.dualTarget;
handleExpand(dualTarget);
@@ -160,6 +163,10 @@ public class QSTileView extends QSTileBaseView {
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
}
+ protected void changeLabelColor(ColorStateList color) {
+ mLabel.setTextColor(color);
+ }
+
protected void handleExpand(boolean dualTarget) {
mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
@@ -178,4 +185,10 @@ public class QSTileView extends QSTileBaseView {
public TextView getAppLabel() {
return mSecondLine;
}
+
+ @Nullable
+ @Override
+ public View getLabelContainer() {
+ return mLabelContainer;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 328c2c353a29..32285cf797e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -21,8 +21,7 @@ import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.Drawable
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.RoundRectShape
+import android.graphics.drawable.RippleDrawable
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.view.Gravity
import android.widget.LinearLayout
@@ -32,25 +31,31 @@ import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
-// Placeholder
-private const val CORNER_RADIUS = 40f
-private val RADII = (1..8).map { CORNER_RADIUS }.toFloatArray()
-
open class QSTileViewHorizontal(
context: Context,
- icon: QSIconView
-) : QSTileView(context, icon, false) {
+ icon: QSIconView,
+ collapsed: Boolean
+) : QSTileView(context, icon, collapsed) {
- protected var backgroundDrawable: ShapeDrawable? = null
+ protected var colorBackgroundDrawable: Drawable? = null
private var paintColor = Color.WHITE
private var paintAnimator: ValueAnimator? = null
+ private var labelAnimator: ValueAnimator? = null
init {
orientation = HORIZONTAL
+ gravity = Gravity.CENTER_VERTICAL or Gravity.START
mDualTargetAllowed = false
+ val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
+ setPadding(padding, paddingTop, padding, paddingBottom)
+
mBg.setImageDrawable(null)
+ mIconFrame.removeAllViews()
+ removeView(mIconFrame)
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ addView(mIcon, 0, LayoutParams(iconSize, iconSize))
+
mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
- mMaxLabelLines = 3
}
override fun createLabel() {
@@ -61,65 +66,112 @@ open class QSTileViewHorizontal(
removeRule(RelativeLayout.ALIGN_PARENT_TOP)
}
}
+ mLabelContainer.setPadding(0, 0, 0, 0)
+ (mLabelContainer.layoutParams as MarginLayoutParams).apply {
+ marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+ }
mLabel.gravity = Gravity.START
mLabel.textDirection = TEXT_DIRECTION_LOCALE
mSecondLine.gravity = Gravity.START
mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
- val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
- mLabelContainer.setPaddingRelative(0, padding, padding, padding)
+
(mLabelContainer.layoutParams as LayoutParams).gravity =
Gravity.CENTER_VERTICAL or Gravity.START
+ if (mCollapsedView) {
+ mSecondLine.visibility = GONE
+ }
+ }
+
+ override fun shouldLabelBeSingleLine(): Boolean {
+ return true
}
override fun updateRippleSize() {
}
override fun newTileBackground(): Drawable? {
- backgroundDrawable = ShapeDrawable(RoundRectShape(RADII, null, null))
- return backgroundDrawable
+ val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+ return ripple
}
override fun setClickable(clickable: Boolean) {
super.setClickable(clickable)
- background = mTileBackground
+ background = if (clickable && mShowRippleEffect) {
+ mTileBackground
+ } else {
+ colorBackgroundDrawable
+ }
}
override fun handleStateChanged(state: QSTile.State) {
super.handleStateChanged(state)
- mSecondLine.setTextColor(mLabel.textColors)
mLabelContainer.background = null
val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
val newColor = getCircleColor(state.state)
if (allowAnimations) {
- animateToNewState(newColor)
+ animateBackground(newColor)
} else {
if (newColor != paintColor) {
- clearAnimator()
- backgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))
+ clearBackgroundAnimator()
+ colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
+ paintColor = newColor
+ }
paintColor = newColor
}
}
}
- private fun animateToNewState(newColor: Int) {
- if (newColor != paintColor) {
- clearAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newColor)
+ private fun animateBackground(newBackgroundColor: Int) {
+ if (newBackgroundColor != paintColor) {
+ clearBackgroundAnimator()
+ paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
.setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
addUpdateListener { animation: ValueAnimator ->
val c = animation.animatedValue as Int
- backgroundDrawable?.setTintList(ColorStateList.valueOf(c))
- paintColor = c
+ colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also {
+ paintColor = c
+ }
}
start()
}
}
}
- private fun clearAnimator() {
+ override fun changeLabelColor(color: ColorStateList) {
+ val allowAnimations = animationsEnabled()
+ val currentColor = mLabel.textColors.defaultColor
+ if (currentColor != color.defaultColor) {
+ clearLabelAnimator()
+ if (allowAnimations) {
+ labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener {
+ setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int))
+ }
+ start()
+ }
+ } else {
+ setLabelsColor(color)
+ }
+ }
+ }
+
+ private fun setLabelsColor(color: ColorStateList) {
+ mLabel.setTextColor(color)
+ if (!mCollapsedView) {
+ mSecondLine.setTextColor(color)
+ }
+ }
+
+ private fun clearBackgroundAnimator() {
paintAnimator?.cancel()?.also { paintAnimator = null }
}
+ private fun clearLabelAnimator() {
+ labelAnimator?.cancel()?.also { labelAnimator = null }
+ }
+
override fun handleExpand(dualTarget: Boolean) {}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index b8485907c519..07abb90f631c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -38,6 +38,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.GlobalSetting;
@@ -63,6 +64,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -70,8 +72,8 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
BroadcastDispatcher broadcastDispatcher,
Lazy<ConnectivityManager> lazyConnectivityManager
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
mLazyConnectivityManager = lazyConnectivityManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
new file mode 100644
index 000000000000..2945c6b4e22a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -0,0 +1,120 @@
+package com.android.systemui.qs.tiles
+
+import android.app.AlarmManager
+import android.app.AlarmManager.AlarmClockInfo
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.AlarmClock
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.text.format.DateFormat
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.NextAlarmController
+import java.util.Locale
+import javax.inject.Inject
+
+class AlarmTile @Inject constructor(
+ host: QSHost,
+ @Background backgroundLooper: Looper,
+ @Main mainHandler: Handler,
+ falsingManager: FalsingManager,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ private val featureFlags: FeatureFlags,
+ private val userTracker: UserTracker,
+ nextAlarmController: NextAlarmController
+) : QSTileImpl<QSTile.State>(
+ host,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+) {
+
+ private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null
+ private val icon = ResourceIcon.get(R.drawable.ic_alarm)
+ @VisibleForTesting
+ internal val defaultIntent = Intent(AlarmClock.ACTION_SET_ALARM)
+ private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
+ lastAlarmInfo = nextAlarm
+ refreshState()
+ }
+
+ init {
+ nextAlarmController.observe(this, callback)
+ }
+
+ override fun isAvailable(): Boolean {
+ return featureFlags.isAlarmTileAvailable
+ }
+
+ override fun newTileState(): QSTile.State {
+ return QSTile.State().apply {
+ handlesLongClick = false
+ }
+ }
+
+ private fun startDefaultSetAlarm() {
+ mActivityStarter.postStartActivityDismissingKeyguard(defaultIntent, 0)
+ }
+
+ override fun handleClick() {
+ lastAlarmInfo?.showIntent?.let {
+ mActivityStarter.postStartActivityDismissingKeyguard(it)
+ } ?: startDefaultSetAlarm()
+ }
+
+ override fun handleUpdateState(state: QSTile.State, arg: Any?) {
+ state.icon = icon
+ state.label = tileLabel
+ lastAlarmInfo?.let {
+ state.secondaryLabel = formatNextAlarm(it)
+ state.state = Tile.STATE_ACTIVE
+ } ?: run {
+ state.secondaryLabel = mContext.getString(R.string.qs_alarm_tile_no_alarm)
+ state.state = Tile.STATE_INACTIVE
+ }
+ state.contentDescription = TextUtils.concat(state.label, ", ", state.secondaryLabel)
+ }
+
+ override fun getTileLabel(): CharSequence {
+ return mContext.getString(R.string.status_bar_alarm)
+ }
+
+ private fun formatNextAlarm(info: AlarmClockInfo): String {
+ val skeleton = if (use24HourFormat()) "EHm" else "Ehma"
+ val pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton)
+ return DateFormat.format(pattern, info.triggerTime).toString()
+ }
+
+ private fun use24HourFormat(): Boolean {
+ return DateFormat.is24HourFormat(mContext, userTracker.userId)
+ }
+
+ override fun getMetricsCategory(): Int {
+ return 0
+ }
+
+ override fun getLongClickIntent(): Intent? {
+ return null
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index bf3e4be9b9db..49d3ff9439d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -29,6 +29,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -59,6 +60,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -66,8 +68,8 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
BatteryController batteryController,
SecureSettings secureSettings
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
int currentUser = host.getUserContext().getUserId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 1424244b6729..56df4d8de6c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -42,6 +42,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -70,14 +71,15 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
BluetoothController bluetoothController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = bluetoothController;
mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
mController.observe(getLifecycle(), mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 70287cd37d01..0d73a5a97706 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -21,7 +21,6 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.os.Handler;
import android.os.Looper;
@@ -35,6 +34,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
@@ -50,13 +50,15 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
@Background Looper backgroundLooper,
@Main Handler mainHandler,
MetricsLogger metricsLogger,
+ FalsingManager falsingManager,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
IndividualSensorPrivacyController sensorPrivacyController,
KeyguardStateController keyguardStateController) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
+ keyguardStateController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index f03ce2c0b267..fa99eed150e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -40,6 +40,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -81,6 +82,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -90,8 +92,8 @@ public class CastTile extends QSTileImpl<BooleanState> {
NetworkController networkController,
HotspotController hotspotController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = castController;
mDetailAdapter = new CastDetailAdapter();
mKeyguard = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 6a574d1d314b..8cc6ff254083 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -45,6 +45,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -76,14 +77,15 @@ public class CellularTile extends QSTileImpl<SignalState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
NetworkController networkController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = networkController;
mDataController = mController.getMobileDataController();
mDetailAdapter = new CellularDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a6d96042d5af..ca7cf8319d32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -32,6 +32,7 @@ import com.android.systemui.R.drawable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -60,6 +61,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -67,8 +69,8 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
UserTracker userTracker,
SecureSettings secureSettings
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mSetting = new SecureSetting(secureSettings, mHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 85f12458c33a..61376f0610a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -29,6 +29,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -49,14 +50,15 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
DataSaverController dataSaverController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mDataSaverController = dataSaverController;
mDataSaverController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 3b9f5dc8ea03..a74a50e7d6c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles
+import android.content.ComponentName
import android.content.Intent
import android.os.Handler
import android.os.Looper
@@ -27,10 +28,12 @@ import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
+import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
@@ -40,24 +43,24 @@ import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.util.settings.GlobalSettings
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
-import javax.inject.Provider
class DeviceControlsTile @Inject constructor(
host: QSHost,
@Background backgroundLooper: Looper,
@Main mainHandler: Handler,
+ falsingManager: FalsingManager,
metricsLogger: MetricsLogger,
statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter,
qsLogger: QSLogger,
private val controlsComponent: ControlsComponent,
private val featureFlags: FeatureFlags,
- private val dialogProvider: Provider<ControlsDialog>,
globalSettings: GlobalSettings
) : QSTileImpl<QSTile.State>(
host,
backgroundLooper,
mainHandler,
+ falsingManager,
metricsLogger,
statusBarStateController,
activityStarter,
@@ -72,7 +75,6 @@ class DeviceControlsTile @Inject constructor(
private var hasControlsApps = AtomicBoolean(false)
private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
- private var controlsDialog: ControlsDialog? = null
private val icon = ResourceIcon.get(R.drawable.ic_device_light)
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
@@ -102,27 +104,19 @@ class DeviceControlsTile @Inject constructor(
}
override fun handleDestroy() {
- dismissDialog()
super.handleDestroy()
}
- private fun createDialog() {
- if (controlsDialog?.isShowing != true) {
- controlsDialog = dialogProvider.get()
- }
- }
-
- private fun dismissDialog() {
- controlsDialog?.dismiss()?.also {
- controlsDialog = null
- }
- }
-
override fun handleClick() {
if (state.state == Tile.STATE_ACTIVE) {
mUiHandler.post {
- createDialog()
- controlsDialog?.show(controlsComponent.getControlsUiController().get())
+ mHost.collapsePanels()
+ val i = Intent().apply {
+ component = ComponentName(mContext, ControlsActivity::class.java)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
+ }
+ mContext.startActivity(i)
}
}
}
@@ -133,9 +127,6 @@ class DeviceControlsTile @Inject constructor(
state.contentDescription = state.label
state.icon = icon
if (controlsComponent.isEnabled() && hasControlsApps.get()) {
- if (controlsDialog == null) {
- mUiHandler.post(this::createDialog)
- }
if (controlsComponent.getVisibility() == AVAILABLE) {
state.state = Tile.STATE_ACTIVE
state.secondaryLabel = ""
@@ -146,7 +137,6 @@ class DeviceControlsTile @Inject constructor(
state.stateDescription = state.secondaryLabel
} else {
state.state = Tile.STATE_UNAVAILABLE
- dismissDialog()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 7ec2691f3a04..4b96cf3dbf16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -54,6 +54,7 @@ import com.android.systemui.SysUIToast;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -87,6 +88,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -94,8 +96,8 @@ public class DndTile extends QSTileImpl<BooleanState> {
ZenModeController zenModeController,
@Main SharedPreferences sharedPreferences
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = zenModeController;
mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index cd45082458f2..31a98db033f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -30,6 +30,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -51,14 +52,15 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
FlashlightController flashlightController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mFlashlightController = flashlightController;
mFlashlightController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index a45d94ace425..4e0f634ce22c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -32,6 +32,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -57,6 +58,7 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -64,8 +66,8 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
HotspotController hotspotController,
DataSaverController dataSaverController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mHotspotController.observe(this, mCallbacks);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index e1a1fd2679c5..1a17e61111d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -40,9 +40,9 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.Icon;
import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.AlphaControlledSignalTileView;
@@ -80,14 +80,15 @@ public class InternetTile extends QSTileImpl<SignalState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
NetworkController networkController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = networkController;
mDataController = mController.getMobileDataController();
mController.observe(getLifecycle(), mSignalCallback);
@@ -415,7 +416,6 @@ public class InternetTile extends QSTileImpl<SignalState> {
}
} else {
state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
- state.label = r.getString(R.string.quick_settings_airplane_safe_label);
}
} else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
@@ -479,9 +479,6 @@ public class InternetTile extends QSTileImpl<SignalState> {
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
} else {
- if (cb.mAirplaneModeEnabled) {
- state.label = r.getString(R.string.quick_settings_airplane_safe_label);
- }
state.icon = new SignalIcon(cb.mMobileSignalIconId);
state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
getMobileDataContentName(cb));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index d502d06efceb..830a1fd57d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -30,6 +30,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -55,6 +56,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -62,8 +64,8 @@ public class LocationTile extends QSTileImpl<BooleanState> {
LocationController locationController,
KeyguardStateController keyguardStateController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = locationController;
mKeyguard = keyguardStateController;
mController.observe(this, mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index e9b712df2154..b8d879226f55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -21,7 +21,6 @@ import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.os.Handler;
import android.os.Looper;
@@ -35,6 +34,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
@@ -50,13 +50,15 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
@Background Looper backgroundLooper,
@Main Handler mainHandler,
MetricsLogger metricsLogger,
+ FalsingManager falsingManager,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
IndividualSensorPrivacyController sensorPrivacyController,
KeyguardStateController keyguardStateController) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
+ keyguardStateController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 63e27796f441..6ac2f9ae202a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -36,6 +36,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -60,14 +61,15 @@ public class NfcTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
BroadcastDispatcher broadcastDispatcher
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index d8548decc5f4..536908639def 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -40,6 +40,7 @@ import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -78,6 +79,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -86,8 +88,8 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
ColorDisplayManager colorDisplayManager,
NightDisplayListenerModule.Builder nightDisplayListenerBuilder
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mLocationController = locationController;
mManager = colorDisplayManager;
mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
new file mode 100644
index 000000000000..60c5d1cafde9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quicksettings.Tile;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Quick access wallet **/
+public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
+
+ private static final String FEATURE_CHROME_OS = "org.chromium.arc";
+ private final CharSequence mLabel = mContext.getString(R.string.wallet_title);
+ // TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes.
+ private final QuickAccessWalletClient mQuickAccessWalletClient;
+ private final KeyguardStateController mKeyguardStateController;
+ private final PackageManager mPackageManager;
+ private final SecureSettings mSecureSettings;
+ private final FeatureFlags mFeatureFlags;
+
+ @Inject
+ public QuickAccessWalletTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ QuickAccessWalletClient quickAccessWalletClient,
+ KeyguardStateController keyguardStateController,
+ PackageManager packageManager,
+ SecureSettings secureSettings,
+ FeatureFlags featureFlags) {
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mQuickAccessWalletClient = quickAccessWalletClient;
+ mKeyguardStateController = keyguardStateController;
+ mPackageManager = packageManager;
+ mSecureSettings = secureSettings;
+ mFeatureFlags = featureFlags;
+ }
+
+
+ @Override
+ public State newTileState() {
+ State state = new State();
+ state.handlesLongClick = false;
+ return state;
+ }
+
+ @Override
+ protected void handleClick() {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+ }
+
+ @Override
+ protected void handleUpdateState(State state, Object arg) {
+ CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
+ state.label = qawLabel == null ? mLabel : qawLabel;
+ state.contentDescription = state.label;
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_wallet);
+ boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
+ if (mQuickAccessWalletClient.isWalletFeatureAvailable()) {
+ state.state = isDeviceLocked ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE;
+ state.secondaryLabel = isDeviceLocked
+ ? null
+ : mContext.getString(R.string.wallet_secondary_label);
+ state.stateDescription = state.secondaryLabel;
+ } else {
+ state.state = Tile.STATE_UNAVAILABLE;
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mFeatureFlags.isQuickAccessWalletEnabled()
+ && mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
+ && !mPackageManager.hasSystemFeature(FEATURE_CHROME_OS)
+ && mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT) != null;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
+ return qawLabel == null ? mLabel : qawLabel;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index aec7b9a4b6b1..479be659ba10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -30,6 +30,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -57,13 +58,14 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mReduceBrightColorsController = reduceBrightColorsController;
mReduceBrightColorsController.observe(getLifecycle(), this);
mIsAvailable = isAvailable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index c46cc4f9aa0a..173cc0597b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -31,6 +31,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -52,14 +53,15 @@ public class RotationLockTile extends QSTileImpl<BooleanState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
RotationLockController rotationLockController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = rotationLockController;
mController.observe(this, mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 0f0a9a2766f1..6845dc593aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -29,6 +29,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -55,6 +56,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -62,8 +64,8 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
RecordingController controller,
KeyguardDismissUtil keyguardDismissUtil
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = controller;
mController.observe(this, mCallback);
mKeyguardDismissUtil = keyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 0c582bdbe12f..a492330c1796 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -29,6 +29,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -59,14 +60,15 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS
protected SensorPrivacyToggleTile(QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
IndividualSensorPrivacyController sensorPrivacyController,
KeyguardStateController keyguardStateController) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mSensorPrivacyController = sensorPrivacyController;
mKeyguard = keyguardStateController;
mSensorPrivacyController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 78975a4798ce..0ef5bc8f926b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -32,6 +32,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -66,6 +67,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -74,8 +76,8 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
BatteryController batteryController,
LocationController locationController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mBatteryController = batteryController;
mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
mLocationController = locationController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index a6cddd3367d3..2590f374a662 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -28,6 +28,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
@@ -51,6 +52,7 @@ public class UserTile extends QSTileImpl<State> implements UserInfoController.On
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -58,8 +60,8 @@ public class UserTile extends QSTileImpl<State> implements UserInfoController.On
UserSwitcherController userSwitcherController,
UserInfoController userInfoController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mUserSwitcherController = userSwitcherController;
mUserInfoController = userInfoController;
mUserInfoController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 341e67c9393f..dab68ed30c2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -77,6 +78,7 @@ public class WifiTile extends QSTileImpl<SignalState> {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
@@ -84,8 +86,8 @@ public class WifiTile extends QSTileImpl<SignalState> {
NetworkController networkController,
AccessPointController accessPointController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mController = networkController;
mWifiController = accessPointController;
mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 5235b6d5a0bb..c88a002ddf92 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -29,6 +29,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -50,14 +51,15 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
ManagedProfileController managedProfileController
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
mProfileController = managedProfileController;
mProfileController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a87bfd83916a..8951605846a1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -294,11 +294,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void setBackButtonAlpha(float alpha, boolean animate) {
- setNavBarButtonAlpha(alpha, animate);
- }
-
- @Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
if (!verifyCaller("onAssistantProgress")) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index c066619d049a..1ec175c01be7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -26,6 +26,8 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
@@ -94,6 +96,25 @@ public class CropView extends View {
}
@Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ SavedState ss = new SavedState(superState);
+ ss.mTopBoundary = getTopBoundary();
+ ss.mBottomBoundary = getBottomBoundary();
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ setBoundaryTo(CropBoundary.TOP, ss.mTopBoundary);
+ setBoundaryTo(CropBoundary.BOTTOM, ss.mBottomBoundary);
+ }
+
+ @Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
float top = mTopCrop + mTopDelta;
@@ -136,7 +157,7 @@ public class CropView extends View {
case MotionEvent.ACTION_UP:
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
// Commit the delta to the stored crop values.
- commitDeltas();
+ commitDeltas(mCurrentDraggingBoundary);
updateListener(event);
}
}
@@ -184,12 +205,12 @@ public class CropView extends View {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- commitDeltas();
+ commitDeltas(boundary);
}
@Override
public void onAnimationCancel(Animator animation) {
- commitDeltas();
+ commitDeltas(boundary);
}
});
animator.setFloatValues(0f, 1f);
@@ -228,11 +249,14 @@ public class CropView extends View {
mCropInteractionListener = listener;
}
- private void commitDeltas() {
- mTopCrop += mTopDelta;
- mBottomCrop += mBottomDelta;
- mTopDelta = 0;
- mBottomDelta = 0;
+ private void commitDeltas(CropBoundary boundary) {
+ if (boundary == CropBoundary.TOP) {
+ mTopCrop += mTopDelta;
+ mTopDelta = 0;
+ } else if (boundary == CropBoundary.BOTTOM) {
+ mBottomCrop += mBottomDelta;
+ mBottomDelta = 0;
+ }
}
private void updateListener(MotionEvent event) {
@@ -377,6 +401,44 @@ public class CropView extends View {
*/
void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
int boundaryPositionPx);
+ }
+ static class SavedState extends BaseSavedState {
+ float mTopBoundary;
+ float mBottomBoundary;
+
+ /**
+ * Constructor called from {@link CropView#onSaveInstanceState()}
+ */
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ mTopBoundary = in.readFloat();
+ mBottomBoundary = in.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeFloat(mTopBoundary);
+ out.writeFloat(mBottomBoundary);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index bc8adc9dad5b..4aead817fe8c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -213,6 +213,20 @@ class ImageExporter {
CompressFormat format;
boolean published;
boolean deleted;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Result{");
+ sb.append("uri=").append(uri);
+ sb.append(", requestId=").append(requestId);
+ sb.append(", fileName='").append(fileName).append('\'');
+ sb.append(", timestamp=").append(timestamp);
+ sb.append(", format=").append(format);
+ sb.append(", published=").append(published);
+ sb.append(", deleted=").append(deleted);
+ sb.append('}');
+ return sb.toString();
+ }
}
private static class Task {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
index 988b93c8ca59..7ee7c319799c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
@@ -21,7 +21,6 @@ import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -43,6 +42,16 @@ public class ImageLoader {
@Nullable Uri uri;
@Nullable File fileName;
@Nullable Bitmap bitmap;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Result{");
+ sb.append("uri=").append(uri);
+ sb.append(", fileName=").append(fileName);
+ sb.append(", bitmap=").append(bitmap);
+ sb.append('}');
+ return sb.toString();
+ }
}
@Inject
@@ -54,7 +63,7 @@ public class ImageLoader {
* Loads an image via URI from ContentResolver.
*
* @param uri the identifier of the image to load
- * @return a listenable future result
+ * @return a listenable future result containing a bitmap
*/
ListenableFuture<Result> load(Uri uri) {
return CallbackToFutureAdapter.getFuture(completer -> {
@@ -76,7 +85,7 @@ public class ImageLoader {
* permissions to read this file/path.
*
* @param file the system file path of the image to load
- * @return a listenable future result
+ * @return a listenable future result containing a bitmap
*/
ListenableFuture<Result> load(File file) {
return CallbackToFutureAdapter.getFuture(completer -> {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 6743afa3ab59..07adc7bd7053 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -31,6 +31,7 @@ import com.android.internal.util.CallbackRegistry;
import com.android.internal.util.CallbackRegistry.NotifierCallback;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -69,9 +70,6 @@ class ImageTileSet {
private final Rect mBounds = new Rect();
private final Handler mHandler;
- private OnContentChangedListener mOnContentChangedListener;
- private OnBoundsChangedListener mOnBoundsChangedListener;
-
void addOnBoundsChangedListener(OnBoundsChangedListener listener) {
if (mOnBoundsListeners == null) {
mOnBoundsListeners = new CallbackRegistry<>(
@@ -204,18 +202,17 @@ class ImageTileSet {
return mBounds.height();
}
- @AnyThread
void clear() {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(this::clear);
- return;
- }
if (mTiles.isEmpty()) {
return;
}
mBounds.setEmpty();
- mTiles.forEach(ImageTile::close);
- mTiles.clear();
+ Iterator<ImageTile> i = mTiles.iterator();
+ while (i.hasNext()) {
+ ImageTile next = i.next();
+ next.close();
+ i.remove();
+ }
notifyBoundsChanged(mBounds);
notifyContentChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 4dc846e0e95d..3ac884b98136 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -21,7 +21,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
@@ -30,10 +29,13 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.SystemClock;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.view.IScrollCaptureConnection;
+import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
import android.view.View;
import android.widget.ImageView;
@@ -41,14 +43,14 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.UUID;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -61,18 +63,15 @@ import javax.inject.Inject;
public class LongScreenshotActivity extends Activity {
private static final String TAG = "LongScreenshotActivity";
- private static final String IMAGE_PATH_KEY = "saved-image";
- private static final String TOP_BOUNDARY_KEY = "top-boundary";
- private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary";
+ public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
+ private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
private final UiEventLogger mUiEventLogger;
private final ScrollCaptureController mScrollCaptureController;
- private final ScrollCaptureClient.Connection mConnection;
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
private final ImageExporter mImageExporter;
- private String mSavedImagePath;
// If true, the activity is re-loading an image from storage, which should either succeed and
// populate the UI or fail and finish the activity.
private boolean mRestoringInstance;
@@ -84,6 +83,14 @@ public class LongScreenshotActivity extends Activity {
private View mShare;
private CropView mCropView;
private MagnifierView mMagnifierView;
+ private ScrollCaptureResponse mScrollCaptureResponse;
+ private File mSavedImagePath;
+
+ private ListenableFuture<File> mCacheSaveFuture;
+ private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
+
+ private ListenableFuture<LongScreenshot> mLongScreenshotFuture;
+ private LongScreenshot mLongScreenshot;
private enum PendingAction {
SHARE,
@@ -92,35 +99,31 @@ public class LongScreenshotActivity extends Activity {
}
@Inject
- public LongScreenshotActivity(UiEventLogger uiEventLogger,
- ImageExporter imageExporter,
- @Main Executor mainExecutor,
- @Background Executor bgExecutor,
+ public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
+ @Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms,
Context context) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
-
- mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor,
- imageExporter);
-
- mConnection = ScreenshotController.takeScrollCaptureConnection();
+ mScrollCaptureController = new ScrollCaptureController(context, bgExecutor, wms);
}
+
@Override
public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
+ super.onCreate(savedInstanceState);
setContentView(R.layout.long_screenshot);
- mPreview = findViewById(R.id.preview);
- mSave = findViewById(R.id.save);
- mCancel = findViewById(R.id.cancel);
- mEdit = findViewById(R.id.edit);
- mShare = findViewById(R.id.share);
- mCropView = findViewById(R.id.crop_view);
- mMagnifierView = findViewById(R.id.magnifier);
+ mPreview = requireViewById(R.id.preview);
+ mSave = requireViewById(R.id.save);
+ mCancel = requireViewById(R.id.cancel);
+ mEdit = requireViewById(R.id.edit);
+ mShare = requireViewById(R.id.share);
+ mCropView = requireViewById(R.id.crop_view);
+ mMagnifierView = requireViewById(R.id.magnifier);
mCropView.setCropInteractionListener(mMagnifierView);
mSave.setOnClickListener(this::onClicked);
@@ -128,71 +131,188 @@ public class LongScreenshotActivity extends Activity {
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- if (savedInstanceState != null) {
- String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY);
- if (!TextUtils.isEmpty(imagePath)) {
- mRestoringInstance = true;
- mBackgroundExecutor.execute(() -> {
- Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
- if (bitmap == null) {
- Log.e(TAG, "Failed to read bitmap from " + imagePath);
- finishAndRemoveTask();
- } else {
- runOnUiThread(() -> {
- BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
- mPreview.setImageDrawable(drawable);
- mMagnifierView.setDrawable(drawable, bitmap.getWidth(),
- bitmap.getHeight());
-
- mCropView.setBoundaryTo(CropView.CropBoundary.TOP,
- savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f));
- mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM,
- savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f));
- mRestoringInstance = false;
- // Reuse the same path for subsequent restoration.
- mSavedImagePath = imagePath;
- Log.d(TAG, "Loaded bitmap from " + imagePath);
- });
- }
- });
- }
- }
mPreview.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
updateCropLocation());
+
+ Intent intent = getIntent();
+ mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
+
+ if (savedInstanceState != null) {
+ String savedImagePath = savedInstanceState.getString(KEY_SAVED_IMAGE_PATH);
+ if (savedImagePath == null) {
+ Log.e(TAG, "Missing saved state entry with key '" + KEY_SAVED_IMAGE_PATH + "'!");
+ finishAndRemoveTask();
+ return;
+ }
+ mSavedImagePath = new File(savedImagePath);
+ ImageLoader imageLoader = new ImageLoader(getContentResolver());
+ mCacheLoadFuture = imageLoader.load(mSavedImagePath);
+ }
}
@Override
public void onStart() {
+ Log.d(TAG, "onStart");
super.onStart();
- if (mPreview.getDrawable() == null && !mRestoringInstance) {
- if (mConnection == null) {
- Log.e(TAG, "Failed to get scroll capture connection, bailing out");
+
+ if (mCacheLoadFuture != null) {
+ Log.d(TAG, "mRestoringInstance = true");
+ final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture;
+ mCacheLoadFuture.addListener(() -> {
+ Log.d(TAG, "cached bitmap load complete");
+ try {
+ onCachedImageLoaded(future.get());
+ } catch (CancellationException | ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Failed to load cached image", e);
+ if (mSavedImagePath != null) {
+ //noinspection ResultOfMethodCallIgnored
+ mSavedImagePath.delete();
+ mSavedImagePath = null;
+ }
+ finishAndRemoveTask();
+ }
+ }, mUiExecutor);
+ mCacheLoadFuture = null;
+ return;
+ }
+
+ if (mLongScreenshotFuture == null) {
+ Log.d(TAG, "mLongScreenshotFuture == null");
+ // First run through, ensure we have a connection to use (see #onCreate)
+ if (mScrollCaptureResponse == null || !mScrollCaptureResponse.isConnected()) {
+ Log.e(TAG, "Did not receive a live scroll capture connection, bailing out!");
finishAndRemoveTask();
return;
}
- doCapture();
+ mLongScreenshotFuture = mScrollCaptureController.run(mScrollCaptureResponse);
+ mLongScreenshotFuture.addListener(() -> {
+ LongScreenshot longScreenshot;
+ try {
+ longScreenshot = mLongScreenshotFuture.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error capturing long screenshot!", e);
+ finishAndRemoveTask();
+ return;
+ }
+ if (longScreenshot.getHeight() == 0) {
+ Log.e(TAG, "Got a zero height result");
+ finishAndRemoveTask();
+ return;
+ }
+ onCaptureCompleted(longScreenshot);
+ }, mUiExecutor);
+ } else {
+ Log.d(TAG, "mLongScreenshotFuture != null");
}
}
+ private void onCaptureCompleted(LongScreenshot longScreenshot) {
+ Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")");
+ mLongScreenshot = longScreenshot;
+ mPreview.setImageDrawable(mLongScreenshot.getDrawable());
+ updateCropLocation();
+ mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
+ mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
+ // Original boundaries go from the image tile set's y=0 to y=pageSize, so
+ // we animate to that as a starting crop position.
+ float topFraction = Math.max(0,
+ -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
+ float bottomFraction = Math.min(1f,
+ 1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight())
+ / (float) mLongScreenshot.getHeight());
+ mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction);
+ mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction);
+ setButtonsEnabled(true);
+
+ // Immediately export to temp image file for saved state
+ mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor,
+ mLongScreenshot.toBitmap());
+ mCacheSaveFuture.addListener(() -> {
+ try {
+ // Get the temp file path to persist, used in onSavedInstanceState
+ mSavedImagePath = mCacheSaveFuture.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error saving temp image file", e);
+ finishAndRemoveTask();
+ }
+ }, mUiExecutor);
+ }
+
+ private void onCachedImageLoaded(ImageLoader.Result imageResult) {
+ Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")");
+ BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
+ mPreview.setImageDrawable(drawable);
+ mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
+ imageResult.bitmap.getHeight());
+ mSavedImagePath = imageResult.fileName;
+
+ setButtonsEnabled(true);
+ }
+
+ private static Bitmap renderBitmap(Drawable drawable, Rect bounds) {
+ final RenderNode output = new RenderNode("Bitmap Export");
+ output.setPosition(0, 0, bounds.width(), bounds.height());
+ RecordingCanvas canvas = output.beginRecording();
+ canvas.translate(-bounds.left, -bounds.top);
+ canvas.clipRect(bounds);
+ drawable.draw(canvas);
+ output.endRecording();
+ return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
+ Log.d(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
- outState.putString(IMAGE_PATH_KEY, mSavedImagePath);
- outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary());
- outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary());
+ if (mSavedImagePath != null) {
+ outState.putString(KEY_SAVED_IMAGE_PATH, mSavedImagePath.getPath());
+ }
}
@Override
- protected void onDestroy() {
- super.onDestroy();
- if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) {
+ protected void onPause() {
+ Log.d(TAG, "onPause finishing=" + isFinishing());
+ super.onPause();
+ if (isFinishing()) {
+ if (mScrollCaptureResponse != null) {
+ mScrollCaptureResponse.close();
+ }
+ cleanupCache();
+
+ if (mLongScreenshotFuture != null) {
+ mLongScreenshotFuture.cancel(true);
+ }
+ if (mLongScreenshot != null) {
+ mLongScreenshot.release();
+ }
+ }
+ }
+
+ void cleanupCache() {
+ if (mCacheSaveFuture != null) {
+ mCacheSaveFuture.cancel(true);
+ }
+ if (mSavedImagePath != null) {
Log.d(TAG, "Deleting " + mSavedImagePath);
- File file = new File(mSavedImagePath);
- file.delete();
+ //noinspection ResultOfMethodCallIgnored
+ mSavedImagePath.delete();
+ mSavedImagePath = null;
}
}
+ @Override
+ protected void onStop() {
+ Log.d(TAG, "onStop");
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy");
+ super.onDestroy();
+ }
+
private void setButtonsEnabled(boolean enabled) {
mSave.setEnabled(enabled);
mCancel.setEnabled(enabled);
@@ -244,67 +364,51 @@ public class LongScreenshotActivity extends Activity {
}
private void startExport(PendingAction action) {
+ Log.d(TAG, "startExport(action = " + action + ")");
Drawable drawable = mPreview.getDrawable();
+ if (drawable == null) {
+ Log.e(TAG, "No drawable, skipping export!");
+ return;
+ }
- Rect croppedPortion = new Rect(
- 0,
- (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()),
- drawable.getIntrinsicWidth(),
- (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary()));
- ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
- mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable),
- ZonedDateTime.now());
- exportFuture.addListener(() -> {
- try {
- ImageExporter.Result result = exportFuture.get();
- setButtonsEnabled(true);
- switch (action) {
- case EDIT:
- doEdit(result.uri);
- break;
- case SHARE:
- doShare(result.uri);
- break;
- case SAVE:
- // Nothing more to do
- finishAndRemoveTask();
- break;
- }
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "failed to export", e);
- setButtonsEnabled(true);
- }
- }, mUiExecutor);
- }
+ Rect bounds = new Rect(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ int height = bounds.height();
+ bounds.top = (int) (height * mCropView.getTopBoundary());
+ bounds.bottom = (int) (height * mCropView.getBottomBoundary());
- private Bitmap getBitmap(Rect bounds, Drawable drawable) {
- final RenderNode output = new RenderNode("Bitmap Export");
- output.setPosition(0, 0, bounds.width(), bounds.height());
- RecordingCanvas canvas = output.beginRecording();
- // Translating the canvas instead of setting drawable bounds since the drawable is still
- // used in the preview.
- canvas.translate(0, -bounds.top);
- drawable.draw(canvas);
- output.endRecording();
- return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
+ if (bounds.isEmpty()) {
+ Log.w(TAG, "Crop bounds empty, skipping export.");
+ return;
+ }
+
+ Bitmap output = renderBitmap(mPreview.getDrawable(), bounds);
+ ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
+ mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now());
+ exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
- private void saveCacheBitmap(ImageTileSet tileSet) {
- long startTime = SystemClock.uptimeMillis();
- Bitmap bitmap = tileSet.toBitmap();
- // TODO(b/181562529) Remove this
- mPreview.setImageDrawable(tileSet.getDrawable());
+ private void onExportCompleted(PendingAction action,
+ ListenableFuture<ImageExporter.Result> exportFuture) {
+ setButtonsEnabled(true);
+ ImageExporter.Result result;
try {
- File file = File.createTempFile("long_screenshot", ".png", null);
- FileOutputStream stream = new FileOutputStream(file);
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
- stream.flush();
- stream.close();
- mSavedImagePath = file.getAbsolutePath();
- Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in "
- + (SystemClock.uptimeMillis() - startTime) + "ms");
- } catch (IOException e) {
- Log.e(TAG, "Failed to save bitmap", e);
+ result = exportFuture.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "failed to export", e);
+ return;
+ }
+
+ switch (action) {
+ case EDIT:
+ doEdit(result.uri);
+ break;
+ case SHARE:
+ doShare(result.uri);
+ break;
+ case SAVE:
+ // Nothing more to do
+ finishAndRemoveTask();
+ break;
}
}
@@ -313,8 +417,8 @@ public class LongScreenshotActivity extends Activity {
if (drawable == null) {
return;
}
-
- float imageRatio = drawable.getBounds().width() / (float) drawable.getBounds().height();
+ Rect bounds = drawable.getBounds();
+ float imageRatio = bounds.width() / (float) bounds.height();
float viewRatio = mPreview.getWidth() / (float) mPreview.getHeight();
if (imageRatio > viewRatio) {
@@ -328,27 +432,4 @@ public class LongScreenshotActivity extends Activity {
mCropView.setExtraPadding(0, 0);
}
}
-
- private void doCapture() {
- mScrollCaptureController.start(mConnection,
- new ScrollCaptureController.ScrollCaptureCallback() {
- @Override
- public void onError() {
- Log.e(TAG, "Error capturing long screenshot!");
- finishAndRemoveTask();
- }
-
- @Override
- public void onComplete(ImageTileSet imageTileSet) {
- Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x "
- + imageTileSet.getHeight());
- mPreview.setImageDrawable(imageTileSet.getDrawable());
- updateCropLocation();
- mMagnifierView.setDrawable(imageTileSet.getDrawable(),
- imageTileSet.getWidth(), imageTileSet.getHeight());
- mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
- mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet));
- }
- });
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
new file mode 100644
index 000000000000..9b3e386bc0d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
@@ -0,0 +1,12 @@
+# Scroll Capture (Long Screenshots)
+# Bug component: 801322
+#
+# Referenced by:
+#
+# core/java/src/android/view/OWNERS
+# core/java/src/com/android/internal/view/OWNERS
+# core/tests/coretests/src/android/view/OWNERS
+# core/tests/coretests/src/com/android/internal/view/OWNERS
+
+mrcasey@google.com
+mrenouf@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 3d6dea3cd3f0..798a063f15b9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -65,6 +65,7 @@ import android.view.DisplayAddress;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -86,10 +87,14 @@ import com.android.systemui.screenshot.ScreenshotController.SavedImageData.Actio
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
import com.android.systemui.util.DeviceConfigProxy;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.List;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -101,7 +106,8 @@ import javax.inject.Inject;
public class ScreenshotController {
private static final String TAG = logTag(ScreenshotController.class);
- private static ScrollCaptureClient.Connection sScrollConnection;
+ private ScrollCaptureResponse mLastScrollCaptureResponse;
+ private ListenableFuture<ScrollCaptureResponse> mLastScrollCaptureRequest;
/**
* POD used in the AsyncTask which saves an image in the background.
@@ -222,12 +228,6 @@ public class ScreenshotController {
| ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_ASSETS_PATHS);
- public static @Nullable ScrollCaptureClient.Connection takeScrollCaptureConnection() {
- ScrollCaptureClient.Connection connection = sScrollConnection;
- sScrollConnection = null;
- return connection;
- }
-
@Inject
ScreenshotController(
Context context,
@@ -352,6 +352,11 @@ public class ScreenshotController {
} else {
mScreenshotView.animateDismissal();
}
+
+ if (mLastScrollCaptureResponse != null) {
+ mLastScrollCaptureResponse.close();
+ mLastScrollCaptureResponse = null;
+ }
}
boolean isPendingSharedTransition() {
@@ -526,16 +531,15 @@ public class ScreenshotController {
// the reference used to locate the target window (below).
withWindowAttached(() -> {
mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken());
- mScrollCaptureClient.request(DEFAULT_DISPLAY,
- /* onConnection */
- (connection) -> mScreenshotHandler.post(() ->
- mScreenshotView.showScrollChip(() ->
- /* onClick */
- runScrollCapture(connection))));
+ if (mLastScrollCaptureRequest != null) {
+ mLastScrollCaptureRequest.cancel(true);
+ }
+ mLastScrollCaptureRequest = mScrollCaptureClient.request(DEFAULT_DISPLAY);
+ mLastScrollCaptureRequest.addListener(() ->
+ onScrollCaptureResponseReady(mLastScrollCaptureRequest), mMainExecutor);
});
}
-
attachWindow();
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@@ -560,6 +564,27 @@ public class ScreenshotController {
cancelTimeout(); // restarted after animation
}
+ private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture) {
+ try {
+ if (mLastScrollCaptureResponse != null) {
+ mLastScrollCaptureResponse.close();
+ }
+ mLastScrollCaptureResponse = responseFuture.get();
+ final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE,
+ mLastScrollCaptureResponse);
+ mScreenshotView.showScrollChip(/* onClick */ () -> {
+ // Clear the reference to prevent close() in dismissScreenshot
+ mLastScrollCaptureResponse = null;
+ mContext.startActivity(intent);
+ dismissScreenshot(false);
+ });
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "requestScrollCapture failed", e);
+ }
+ }
+
private void withWindowAttached(Runnable action) {
View decorView = mWindow.getDecorView();
if (decorView.isAttachedToWindow()) {
@@ -606,15 +631,6 @@ public class ScreenshotController {
}
}
- private void runScrollCapture(ScrollCaptureClient.Connection connection) {
- sScrollConnection = connection; // For LongScreenshotActivity to pick up.
-
- Intent intent = new Intent(mContext, LongScreenshotActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivity(intent);
- dismissScreenshot(false);
- }
-
/**
* Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
* failure).
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index 54b99bbe74cc..926d5c4701aa 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,18 +30,23 @@ import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.media.Image;
import android.media.ImageReader;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.util.Log;
import android.view.IScrollCaptureCallbacks;
import android.view.IScrollCaptureConnection;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
+
import com.android.internal.annotations.VisibleForTesting;
-import java.util.function.Consumer;
+import com.google.common.util.concurrent.ListenableFuture;
import javax.inject.Inject;
@@ -57,61 +62,6 @@ public class ScrollCaptureClient {
private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
-
- /**
- * A connection to a remote window. Starts a capture session.
- */
- public interface Connection {
- /**
- * Start a session.
-
- * @param sessionConsumer listener to receive the session once active
- * @param maxPages the capture buffer size expressed as a multiple of the content height
- */
- // TODO ListenableFuture
- void start(Consumer<Session> sessionConsumer, float maxPages);
-
- /**
- * Close the connection. Must end capture if started to avoid potential unwanted visual
- * artifacts.
- *
- * @see Session#end(Runnable)
- */
- void close();
- }
-
- static class CaptureResult {
- public final Image image;
- /**
- * The area requested, in content rect space, relative to scroll-bounds.
- */
- public final Rect requested;
- /**
- * The actual area captured, in content rect space, relative to scroll-bounds. This may be
- * cropped or empty depending on available content.
- */
- public final Rect captured;
-
- // Error?
-
- private CaptureResult(Image image, Rect request, Rect captured) {
- this.image = image;
- this.requested = request;
- this.captured = captured;
- }
-
- @Override
- public String toString() {
- return "CaptureResult{"
- + "requested=" + requested
- + " (" + requested.width() + "x" + requested.height() + ")"
- + ", captured=" + captured
- + " (" + captured.width() + "x" + captured.height() + ")"
- + ", image=" + image
- + '}';
- }
- }
-
/**
* Represents the connection to a target window and provides a mechanism for requesting tiles.
*/
@@ -121,10 +71,8 @@ public class ScrollCaptureClient {
* and from left 0, to {@link #getPageWidth()}
*
* @param top the top (y) position of the tile to capture, in content rect space
- * @param consumer listener to be informed of the result
*/
- // TODO ListenableFuture
- void requestTile(int top, Consumer<CaptureResult> consumer);
+ ListenableFuture<CaptureResult> requestTile(int top);
/**
* Returns the maximum number of tiles which may be requested and retained without
@@ -139,6 +87,7 @@ public class ScrollCaptureClient {
*/
int getTileHeight();
+
/**
* @return the height of scrollable content being captured
*/
@@ -155,11 +104,42 @@ public class ScrollCaptureClient {
Rect getWindowBounds();
/**
- * End the capture session, return the target app to original state. The listener
- * will be called when the target app is ready to before visible and interactive.
+ * End the capture session, return the target app to original state. The returned Future
+ * will complete once the target app is ready to become visible and interactive.
*/
- // TODO ListenableFuture
- void end(Runnable listener);
+ ListenableFuture<Void> end();
+
+ void release();
+ }
+
+ static class CaptureResult {
+ public final Image image;
+ /**
+ * The area requested, in content rect space, relative to scroll-bounds.
+ */
+ public final Rect requested;
+ /**
+ * The actual area captured, in content rect space, relative to scroll-bounds. This may be
+ * cropped or empty depending on available content.
+ */
+ public final Rect captured;
+
+ CaptureResult(Image image, Rect request, Rect captured) {
+ this.image = image;
+ this.requested = request;
+ this.captured = captured;
+ }
+
+ @Override
+ public String toString() {
+ return "CaptureResult{"
+ + "requested=" + requested
+ + " (" + requested.width() + "x" + requested.height() + ")"
+ + ", captured=" + captured
+ + " (" + captured.width() + "x" + captured.height() + ")"
+ + ", image=" + image
+ + '}';
+ }
}
private final IWindowManager mWindowManagerService;
@@ -185,10 +165,9 @@ public class ScrollCaptureClient {
* Check for scroll capture support.
*
* @param displayId id for the display containing the target window
- * @param consumer receives a connection when available
*/
- public void request(int displayId, Consumer<Connection> consumer) {
- request(displayId, MATCH_ANY_TASK, consumer);
+ public ListenableFuture<ScrollCaptureResponse> request(int displayId) {
+ return request(displayId, MATCH_ANY_TASK);
}
/**
@@ -196,194 +175,209 @@ public class ScrollCaptureClient {
*
* @param displayId id for the display containing the target window
* @param taskId id for the task containing the target window or {@link #MATCH_ANY_TASK}.
- * @param consumer receives a connection when available
+ * @return a listenable future providing the response
*/
- public void request(int displayId, int taskId, Consumer<Connection> consumer) {
- try {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken
- + ", taskId=" + taskId + ", consumer=" + consumer + ")");
+ public ListenableFuture<ScrollCaptureResponse> request(int displayId, int taskId) {
+ return CallbackToFutureAdapter.getFuture((completer) -> {
+ try {
+ mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
+ new IScrollCaptureResponseListener.Stub() {
+ @Override
+ public void onScrollCaptureResponse(ScrollCaptureResponse response) {
+ completer.set(response);
+ }
+ });
+
+ } catch (RemoteException e) {
+ completer.setException(e);
}
- mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
- new ClientCallbacks(consumer));
- } catch (RemoteException e) {
- Log.e(TAG, "Ignored remote exception", e);
- }
+ return "ScrollCaptureClient#request"
+ + "(displayId=" + displayId + ", taskId=" + taskId + ")";
+ });
+ }
+
+ /**
+ * Start a scroll capture session.
+ *
+ * @param response a response provided from a request containing a connection
+ * @param maxPages the capture buffer size expressed as a multiple of the content height
+ * @return a listenable future providing the session
+ */
+ public ListenableFuture<Session> start(ScrollCaptureResponse response, float maxPages) {
+ IScrollCaptureConnection connection = response.getConnection();
+ return CallbackToFutureAdapter.getFuture((completer) -> {
+ if (connection == null || !connection.asBinder().isBinderAlive()) {
+ completer.setException(new DeadObjectException("No active connection!"));
+ return "";
+ }
+ SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(),
+ response.getBoundsInWindow(), maxPages);
+ session.start(completer);
+ return "IScrollCaptureCallbacks#onCaptureStarted";
+ });
}
- private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements
- Connection, Session, IBinder.DeathRecipient {
+ private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session,
+ IBinder.DeathRecipient {
private IScrollCaptureConnection mConnection;
- private Consumer<Connection> mConnectionConsumer;
- private Consumer<Session> mSessionConsumer;
- private Consumer<CaptureResult> mResultConsumer;
- private Runnable mShutdownListener;
private ImageReader mReader;
- private Rect mScrollBounds;
- private int mTileHeight;
- private int mTileWidth;
+ private final int mTileHeight;
+ private final int mTileWidth;
private Rect mRequestRect;
private boolean mStarted;
private ICancellationSignal mCancellationSignal;
- private Rect mWindowBounds;
- private Rect mBoundsInWindow;
- private int mMaxTiles;
+ private final Rect mWindowBounds;
+ private final Rect mBoundsInWindow;
+ private final int mMaxTiles;
+
+ private Completer<Session> mStartCompleter;
+ private Completer<CaptureResult> mTileRequestCompleter;
+ private Completer<Void> mEndCompleter;
+
+ private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds,
+ Rect boundsInWindow, float maxPages) throws RemoteException {
+ mConnection = requireNonNull(connection);
+ mConnection.asBinder().linkToDeath(SessionWrapper.this, 0);
+ mWindowBounds = requireNonNull(windowBounds);
+ mBoundsInWindow = requireNonNull(boundsInWindow);
+
+ int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height();
+ int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
+
+ mTileWidth = mBoundsInWindow.width();
+ mTileHeight = pxPerTile / mBoundsInWindow.width();
+ mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
- private ClientCallbacks(Consumer<Connection> connectionConsumer) {
- mConnectionConsumer = connectionConsumer;
+ if (DEBUG_SCROLL) {
+ Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
+ Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
+ }
}
- @BinderThread
@Override
- public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")");
+ public void binderDied() {
+ Log.d(TAG, "binderDied! The target process just crashed :-(");
+ // Clean up
+ mConnection = null;
+
+ // Pass along the bad news.
+ if (mStartCompleter != null) {
+ mStartCompleter.setException(new DeadObjectException("The remote process died"));
}
- if (response.isConnected()) {
- mConnection = response.getConnection();
- mConnection.asBinder().linkToDeath(this, 0);
- mWindowBounds = response.getWindowBounds();
- mBoundsInWindow = response.getBoundsInWindow();
-
- int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height();
- int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
- mTileWidth = mBoundsInWindow.width();
- mTileHeight = pxPerTile / mBoundsInWindow.width();
- if (DEBUG_SCROLL) {
- Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
- Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
- Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px");
- }
- mConnectionConsumer.accept(this);
+ if (mTileRequestCompleter != null) {
+ mTileRequestCompleter.setException(
+ new DeadObjectException("The remote process died"));
+ }
+ if (mEndCompleter != null) {
+ mEndCompleter.setException(new DeadObjectException("The remote process died"));
}
- mConnectionConsumer = null;
}
- @Override
- public void start(Consumer<Session> sessionConsumer, float maxPages) {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
- + " maxPages=" + maxPages + ")");
- }
- mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+ private void start(Completer<Session> completer) {
mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
- mSessionConsumer = sessionConsumer;
-
+ mStartCompleter = completer;
try {
- mCancellationSignal = mConnection.startCapture(mReader.getSurface());
+ mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
+ completer.addCancellationListener(() -> {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }, Runnable::run);
mStarted = true;
} catch (RemoteException e) {
- Log.w(TAG, "Failed to start", e);
mReader.close();
+ completer.setException(e);
}
}
@BinderThread
@Override
public void onCaptureStarted() {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "onCaptureStarted()");
- }
- mSessionConsumer.accept(this);
- mSessionConsumer = null;
+ Log.d(TAG, "onCaptureStarted");
+ mStartCompleter.set(this);
}
@Override
- public void requestTile(int top, Consumer<CaptureResult> consumer) {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
- }
- cancelPendingRequest();
+ public ListenableFuture<CaptureResult> requestTile(int top) {
mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
- mResultConsumer = consumer;
- try {
- mCancellationSignal = mConnection.requestImage(mRequestRect);
- } catch (RemoteException e) {
- Log.e(TAG, "Caught remote exception from requestImage", e);
- }
+ return CallbackToFutureAdapter.getFuture((completer -> {
+ if (mConnection == null || !mConnection.asBinder().isBinderAlive()) {
+ completer.setException(new DeadObjectException("Connection is closed!"));
+ return "";
+ }
+ try {
+ mTileRequestCompleter = completer;
+ mCancellationSignal = mConnection.requestImage(mRequestRect);
+ completer.addCancellationListener(() -> {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }, Runnable::run);
+ } catch (RemoteException e) {
+ completer.setException(e);
+ }
+ return "IScrollCaptureCallbacks#onImageRequestCompleted";
+ }));
}
+ @BinderThread
@Override
public void onImageRequestCompleted(int flags, Rect contentArea) {
Image image = mReader.acquireLatestImage();
- if (DEBUG_SCROLL) {
- Log.d(TAG, "onCaptureBufferSent(flags=" + flags
- + ", contentArea=" + contentArea + ") image=" + image);
- }
- // Save and clear first, since the consumer will likely request the next
- // tile, otherwise the new consumer will be wiped out.
- Consumer<CaptureResult> consumer = mResultConsumer;
- mResultConsumer = null;
- consumer.accept(new CaptureResult(image, mRequestRect, contentArea));
+ mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea));
}
@Override
- public void end(Runnable listener) {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "end(listener=" + listener + ")");
- }
- if (mStarted) {
- mShutdownListener = listener;
- mReader.close();
+ public ListenableFuture<Void> end() {
+ Log.d(TAG, "end()");
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ if (!mStarted) {
+ try {
+ mConnection.asBinder().unlinkToDeath(SessionWrapper.this, 0);
+ mConnection.close();
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ mConnection = null;
+ completer.set(null);
+ return "";
+ }
+
+ mEndCompleter = completer;
try {
- // listener called from onConnectionClosed callback
mConnection.endCapture();
} catch (RemoteException e) {
- Log.d(TAG, "Ignored exception from endCapture()", e);
- disconnect();
- listener.run();
+ completer.setException(e);
}
- } else {
- disconnect();
- listener.run();
- }
+ return "IScrollCaptureCallbacks#onCaptureEnded";
+ });
}
- @BinderThread
- @Override
- public void onCaptureEnded() {
- close();
- if (mShutdownListener != null) {
- mShutdownListener.run();
- mShutdownListener = null;
- }
+ public void release() {
+ mReader.close();
}
+ @BinderThread
@Override
- public void close() {
- if (mConnection != null) {
- try {
- mConnection.close();
- } catch (RemoteException e) {
- /* ignore */
- }
- disconnect();
- }
- }
-
- // Misc
-
- private void disconnect() {
- if (mConnection != null) {
- mConnection.asBinder().unlinkToDeath(this, 0);
+ public void onCaptureEnded() {
+ try {
+ mConnection.close();
+ } catch (RemoteException e) {
+ /* ignore */
}
mConnection = null;
+ mEndCompleter.set(null);
}
- /**
- * The process hosting the window went away abruptly!
- */
- @Override
- public void binderDied() {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "binderDied()");
- }
- disconnect();
- }
+ // Misc
@Override
public int getPageHeight() {
@@ -408,16 +402,5 @@ public class ScrollCaptureClient {
public int getMaxTiles() {
return mMaxTiles;
}
-
- private void cancelPendingRequest() {
- if (mCancellationSignal != null) {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException e) {
- /* ignore */
- }
- mCancellationSignal = null;
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index b62e2c34ed6e..4f699041fdb3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,18 +16,27 @@
package com.android.systemui.screenshot;
-import android.annotation.UiThread;
import android.content.Context;
-import android.net.Uri;
+import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.util.Log;
+import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
-import java.time.ZonedDateTime;
-import java.util.UUID;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
/**
@@ -47,44 +56,136 @@ public class ScrollCaptureController {
// or if the desired bitmap size is reached.
private boolean mFinishOnBoundary;
- private Session mSession;
-
public static final int MAX_HEIGHT = 12000;
private final Context mContext;
-
- private final Executor mUiExecutor;
private final Executor mBgExecutor;
- private final ImageExporter mImageExporter;
private final ImageTileSet mImageTileSet;
+ private final ScrollCaptureClient mClient;
+
+ private Completer<LongScreenshot> mCaptureCompleter;
+
+ private ListenableFuture<Session> mSessionFuture;
+ private Session mSession;
+ private ListenableFuture<CaptureResult> mTileFuture;
+ private ListenableFuture<Void> mEndFuture;
+
+ static class LongScreenshot {
+ private final ImageTileSet mImageTileSet;
+ private final Session mSession;
+
+ LongScreenshot(Session session, ImageTileSet imageTileSet) {
+ mSession = session;
+ mImageTileSet = imageTileSet;
+ }
- private ZonedDateTime mCaptureTime;
- private UUID mRequestId;
- private ScrollCaptureCallback mCaptureCallback;
+ /** Returns a bitmap containing the combinded result. */
+ public Bitmap toBitmap() {
+ return mImageTileSet.toBitmap();
+ }
+
+ public Bitmap toBitmap(Rect bounds) {
+ return mImageTileSet.toBitmap(bounds);
+ }
- public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor,
- ImageExporter exporter) {
+ /** Releases image resources from the screenshot. */
+ public void release() {
+ Log.d(TAG, "LongScreenshot :: release()");
+ mImageTileSet.clear();
+ mSession.release();
+ }
+
+ public int getLeft() {
+ return mImageTileSet.getLeft();
+ }
+
+ public int getTop() {
+ return mImageTileSet.getTop();
+ }
+
+ public int getBottom() {
+ return mImageTileSet.getBottom();
+ }
+
+ public int getWidth() {
+ return mImageTileSet.getWidth();
+ }
+
+ public int getHeight() {
+ return mImageTileSet.getHeight();
+ }
+
+ /** @return the height of the visible area of the scrolling page, in pixels */
+ public int getPageHeight() {
+ return mSession.getPageHeight();
+ }
+
+ @Override
+ public String toString() {
+ return "LongScreenshot{w=" + mImageTileSet.getWidth()
+ + ", h=" + mImageTileSet.getHeight() + "}";
+ }
+
+ public Drawable getDrawable() {
+ return mImageTileSet.getDrawable();
+ }
+ }
+
+ ScrollCaptureController(Context context, Executor bgExecutor, IWindowManager wms) {
mContext = context;
- mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
- mImageExporter = exporter;
mImageTileSet = new ImageTileSet(context.getMainThreadHandler());
+ mClient = new ScrollCaptureClient(mContext, wms);
}
/**
- * Run scroll capture!
+ * Run scroll capture. Performs a batch capture, collecting image tiles.
*
- * @param connection connection to the remote window to be used
- * @param callback request callback to report back to the service
+ * @param response a scroll capture response from a previous request which is
+ * {@link ScrollCaptureResponse#isConnected() connected}.
+ * @return a future ImageTile set containing the result
*/
- public void start(Connection connection, ScrollCaptureCallback callback) {
- mCaptureTime = ZonedDateTime.now();
- mRequestId = UUID.randomUUID();
- mCaptureCallback = callback;
-
- float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
- SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
- connection.start(this::startCapture, maxPages);
+ ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
+ Log.d(TAG, "run: " + response);
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ Log.d(TAG, "getFuture(ImageTileSet) ");
+ mCaptureCompleter = completer;
+ mBgExecutor.execute(() -> {
+ Log.d(TAG, "bgExecutor.execute");
+ float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
+ SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
+ Log.d(TAG, "client start, maxPages=" + maxPages);
+ mSessionFuture = mClient.start(response, maxPages);
+ mSessionFuture.addListener(this::onStartComplete, mContext.getMainExecutor());
+ });
+ return "<batch scroll capture>";
+ });
+ }
+
+ private void onStartComplete() {
+ try {
+ mSession = mSessionFuture.get();
+ Log.d(TAG, "got session " + mSession);
+ requestNextTile(0);
+ } catch (InterruptedException | ExecutionException e) {
+ // Failure to start, propagate to caller
+ Log.d(TAG, "session start failed!");
+ mCaptureCompleter.setException(e);
+ }
+ }
+
+ private void requestNextTile(int topPx) {
+ Log.d(TAG, "requestNextTile: " + topPx);
+ mTileFuture = mSession.requestTile(topPx);
+ mTileFuture.addListener(() -> {
+ try {
+ Log.d(TAG, "onCaptureResult");
+ onCaptureResult(mTileFuture.get());
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "requestTile failed!", e);
+ mCaptureCompleter.setException(e);
+ }
+ }, mContext.getMainExecutor());
}
private void onCaptureResult(CaptureResult result) {
@@ -146,49 +247,24 @@ public class ScrollCaptureController {
}
if (finish) {
- Session session = mSession;
- mSession = null;
Log.d(TAG, "Stop.");
- mUiExecutor.execute(() -> afterCaptureComplete(session));
+ finishCapture();
return;
}
int nextTop = (mScrollingUp)
? result.captured.top - mSession.getTileHeight() : result.captured.bottom;
- Log.d(TAG, "requestTile: " + nextTop);
- mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult);
- }
-
- private void startCapture(Session session) {
- mSession = session;
- session.requestTile(0, this::onCaptureResult);
+ requestNextTile(nextTop);
}
- @UiThread
- void afterCaptureComplete(Session session) {
- Log.d(TAG, "afterCaptureComplete");
-
- if (mImageTileSet.isEmpty()) {
- mCaptureCallback.onError();
- } else {
- mCaptureCallback.onComplete(mImageTileSet);
- }
+ private void finishCapture() {
+ Log.d(TAG, "finishCapture()");
+ mEndFuture = mSession.end();
+ mEndFuture.addListener(() -> {
+ Log.d(TAG, "endCapture completed");
+ // Provide result to caller and complete the top-level future
+ // Caller is responsible for releasing this resource (ImageReader/HardwareBuffers)
+ mCaptureCompleter.set(new LongScreenshot(mSession, mImageTileSet));
+ }, mContext.getMainExecutor());
}
-
- /**
- * Callback for image capture completion or error.
- */
- public interface ScrollCaptureCallback {
- void onComplete(ImageTileSet imageTileSet);
- void onError();
- }
-
- /**
- * Callback for image export completion or error.
- */
- public interface ExportCallback {
- void onExportComplete(Uri outputUri);
- void onError();
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c4fa6df56775..059903961eae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -141,6 +141,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 57 << MSG_SHIFT;
//TODO(b/169175022) Update name and when feature name is locked.
private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT;
+ private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -369,6 +370,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
* Handles a window manager shell logging command.
*/
default void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {}
+
+ /**
+ * @see IStatusBar#setNavigationBarLumaSamplingEnabled(int, boolean)
+ */
+ default void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {}
}
public CommandQueue(Context context) {
@@ -1019,6 +1025,14 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
+ public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED, displayId,
+ enable ? 1 : 0).sendToTarget();
+ }
+ }
+
+ @Override
public void passThroughShellCommand(String[] args, ParcelFileDescriptor pfd) {
final FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
final PrintWriter pw = new PrintWriter(fos);
@@ -1400,6 +1414,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
args.recycle();
break;
+ case MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setNavigationBarLumaSamplingEnabled(msg.arg1,
+ msg.arg2 != 0);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 1d59257c9c4f..c3de81c3c66a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -83,6 +83,10 @@ public class FeatureFlags {
return mFlagReader.isEnabled(R.bool.flag_monet);
}
+ public boolean isQuickAccessWalletEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_wallet);
+ }
+
public boolean isNavigationBarOverlayEnabled() {
return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay);
}
@@ -90,4 +94,8 @@ public class FeatureFlags {
public boolean isPMLiteEnabled() {
return mFlagReader.isEnabled(R.bool.flag_pm_lite);
}
+
+ public boolean isAlarmTileAvailable() {
+ return mFlagReader.isEnabled(R.bool.flag_alarm_tile);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 992015320fa0..e090d0b13205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -31,6 +31,7 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.ConversationLayout;
+import com.android.internal.widget.ImageFloatingTextView;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentView;
@@ -430,6 +431,8 @@ public class NotificationGroupingUtil {
public static final int[] MARGIN_ADJUSTED_VIEWS = {
R.id.notification_headerless_view_column,
+ R.id.text,
+ R.id.big_text,
R.id.title,
R.id.notification_main_column,
R.id.notification_header};
@@ -458,6 +461,10 @@ public class NotificationGroupingUtil {
if (target == null) {
return;
}
+ if (target instanceof ImageFloatingTextView) {
+ ((ImageFloatingTextView) target).setHasImage(iconVisible);
+ return;
+ }
final Integer data = (Integer) target.getTag(iconVisible
? com.android.internal.R.id.tag_margin_end_when_icon_visible
: com.android.internal.R.id.tag_margin_end_when_icon_gone);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 34b29ca9721b..2856ebb4066d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Handler;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -69,8 +70,10 @@ import com.android.systemui.statusbar.policy.RemoteInputView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Stream;
import dagger.Lazy;
@@ -630,24 +633,17 @@ public class NotificationRemoteInputManager implements Dumpable {
Notification.Builder b = Notification.Builder
.recoverBuilder(mContext, sbn.getNotification().clone());
if (remoteInputText != null || uri != null) {
- RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[])
- sbn.getNotification().extras.getParcelableArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
- RemoteInputHistoryItem[] newHistoryItems;
-
- if (oldHistoryItems == null) {
- newHistoryItems = new RemoteInputHistoryItem[1];
- } else {
- newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1];
- System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length);
- }
- RemoteInputHistoryItem newItem;
- if (uri != null) {
- newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText);
- } else {
- newItem = new RemoteInputHistoryItem(remoteInputText);
- }
- newHistoryItems[0] = newItem;
+ RemoteInputHistoryItem newItem = uri != null
+ ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText)
+ : new RemoteInputHistoryItem(remoteInputText);
+ Parcelable[] oldHistoryItems = sbn.getNotification().extras
+ .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null
+ ? Stream.concat(
+ Stream.of(newItem),
+ Arrays.stream(oldHistoryItems).map(p -> (RemoteInputHistoryItem) p))
+ .toArray(RemoteInputHistoryItem[]::new)
+ : new RemoteInputHistoryItem[] { newItem };
b.setRemoteInputHistory(newHistoryItems);
}
b.setShowRemoteInputSpinner(showSpinner);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3496581b1b8a..e1199077efb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN_REVERSE;
-import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -34,7 +31,6 @@ import android.view.WindowInsets;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -55,8 +51,6 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer;
public class NotificationShelf extends ActivatableNotificationView implements
View.OnLayoutChangeListener, StateListener {
- private static final boolean USE_ANIMATIONS_WHEN_OPENING =
- SystemProperties.getBoolean("debug.icon_opening_animations", true);
private static final boolean ICON_ANMATIONS_WHILE_SCROLLING
= SystemProperties.getBoolean("debug.icon_scroll_animations", true);
private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
@@ -174,18 +168,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
float viewEnd = lastViewState.yTranslation + lastViewState.height;
viewState.copyFrom(lastViewState);
viewState.height = getIntrinsicHeight();
-
viewState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
getFullyClosedTranslation());
viewState.zTranslation = ambientState.getBaseZHeight();
- // For the small display size, it's not enough to make the icon not covered by
- // the top cutout so the denominator add the height of cutout.
- // Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
- // mAmbientState.getTopPadding().
- float openedAmount = (viewState.yTranslation - getFullyClosedTranslation())
- / (getIntrinsicHeight() * 2 + mCutoutHeight);
- openedAmount = Math.min(1.0f, openedAmount);
- viewState.openedAmount = openedAmount;
viewState.clipTopAmount = 0;
viewState.alpha = 1;
viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
@@ -220,11 +205,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
View lastChild = mAmbientState.getLastVisibleBackgroundChild();
mNotGoneIndex = -1;
float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
- float expandAmount = 0.0f;
- if (shelfStart >= interpolationStart) {
- expandAmount = (shelfStart - interpolationStart) / getIntrinsicHeight();
- expandAmount = Math.min(1.0f, expandAmount);
- }
// find the first view that doesn't overlap with the shelf
int notGoneIndex = 0;
int colorOfViewBeforeLast = NO_COLOR;
@@ -239,7 +219,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
boolean scrollingFast = currentScrollVelocity > mScrollFastThreshold
|| (mAmbientState.isExpansionChanging()
&& Math.abs(mAmbientState.getExpandingVelocity()) > mScrollFastThreshold);
- boolean scrolling = currentScrollVelocity > 0;
boolean expandingAnimated = mAmbientState.isExpansionChanging()
&& !mAmbientState.isPanelTracking();
int baseZHeight = mAmbientState.getBaseZHeight();
@@ -249,8 +228,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
ActivatableNotificationView previousAnv = null;
for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
- ExpandableView child = (ExpandableView) mHostLayoutController.getChildAt(i);
-
+ ExpandableView child = mHostLayoutController.getChildAt(i);
if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
continue;
}
@@ -268,9 +246,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
int clipTop = updateNotificationClipHeight(child, notificationClipEnd, notGoneIndex);
clipTopAmount = Math.max(clipTop, clipTopAmount);
-
- float inShelfAmount = updateShelfTransformation(child, expandAmount, scrolling,
- scrollingFast, expandingAnimated, isLastChild);
+ final float inShelfAmount = updateShelfTransformation(child, scrollingFast,
+ expandingAnimated, isLastChild);
// If the current row is an ExpandableNotificationRow, update its color, roundedness,
// and icon state.
if (child instanceof ExpandableNotificationRow) {
@@ -498,56 +475,40 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @return the amount how much this notification is in the shelf
*/
- private float updateShelfTransformation(ExpandableView view, float expandAmount,
- boolean scrolling, boolean scrollingFast, boolean expandingAnimated,
- boolean isLastChild) {
- StatusBarIconView icon = view.getShelfIcon();
- NotificationIconContainer.IconState iconState = getIconState(icon);
+ private float updateShelfTransformation(ExpandableView view, boolean scrollingFast,
+ boolean expandingAnimated, boolean isLastChild) {
// Let calculate how much the view is in the shelf
float viewStart = view.getTranslationY();
int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
float iconTransformStart = calculateIconTransformationStart(view);
- float transformDistance = getIntrinsicHeight() * 1.5f;
- transformDistance *= NotificationUtils.interpolate(1.f, 1.5f, expandAmount);
- transformDistance = Math.min(transformDistance, fullHeight);
-
// Let's make sure the transform distance is
// at most to the icon (relevant for conversations)
- transformDistance = Math.min(viewStart + fullHeight - iconTransformStart,
- transformDistance);
+ float transformDistance = Math.min(
+ viewStart + fullHeight - iconTransformStart,
+ getIntrinsicHeight());
if (isLastChild) {
fullHeight = Math.min(fullHeight, view.getMinHeight() - getIntrinsicHeight());
- transformDistance = Math.min(transformDistance, view.getMinHeight()
- - getIntrinsicHeight());
+ transformDistance = Math.min(
+ transformDistance,
+ view.getMinHeight() - getIntrinsicHeight());
}
float viewEnd = viewStart + fullHeight;
- handleCustomTransformHeight(view, expandingAnimated, iconState);
-
- float fullTransitionAmount;
- float iconTransitionAmount;
- float contentTransformationAmount;
+ float fullTransitionAmount = 0.0f;
+ float iconTransitionAmount = 0.0f;
float shelfStart = getTranslationY();
- boolean fullyInOrOut = true;
- if (viewEnd >= shelfStart && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
+
+ if (viewEnd >= shelfStart
+ && (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
&& (mAmbientState.isShadeExpanded()
|| (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
- if (viewStart < shelfStart) {
- if (iconState != null && iconState.hasCustomTransformHeight()) {
- fullHeight = iconState.customTransformHeight;
- transformDistance = iconState.customTransformHeight;
- }
+ if (viewStart < shelfStart) {
float fullAmount = (shelfStart - viewStart) / fullHeight;
fullAmount = Math.min(1.0f, fullAmount);
- float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation(
- fullAmount);
- interpolatedAmount = NotificationUtils.interpolate(
- interpolatedAmount, fullAmount, expandAmount);
- fullTransitionAmount = 1.0f - interpolatedAmount;
-
+ fullTransitionAmount = 1.0f - fullAmount;
if (isLastChild) {
// Reduce icon transform distance to completely fade in shelf icon
// by the time the notification icon fades out, and vice versa
@@ -558,37 +519,14 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f);
iconTransitionAmount = 1.0f - iconTransitionAmount;
- fullyInOrOut = false;
} else {
+ // Fully in shelf.
fullTransitionAmount = 1.0f;
iconTransitionAmount = 1.0f;
}
-
- // Transforming the content
- contentTransformationAmount = (shelfStart - viewStart) / transformDistance;
- contentTransformationAmount = Math.min(1.0f, contentTransformationAmount);
- contentTransformationAmount = 1.0f - contentTransformationAmount;
- } else {
- fullTransitionAmount = 0.0f;
- iconTransitionAmount = 0.0f;
- contentTransformationAmount = 0.0f;
- }
- if (iconState != null && fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
- iconState.isLastExpandIcon = false;
- iconState.customTransformHeight = NO_VALUE;
}
-
- // Update the content transformation amount
- if (view.isAboveShelf() || view.showingPulsing()
- || (!isLastChild && iconState != null && !iconState.translateContent)) {
- contentTransformationAmount = 0.0f;
- }
- view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
-
- // Update the positioning of the icon
- updateIconPositioning(view, iconTransitionAmount, fullTransitionAmount,
- transformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
-
+ updateIconPositioning(view, iconTransitionAmount,
+ scrollingFast, expandingAnimated, isLastChild);
return fullTransitionAmount;
}
@@ -607,87 +545,31 @@ public class NotificationShelf extends ActivatableNotificationView implements
return start;
}
- private void handleCustomTransformHeight(ExpandableView view, boolean expandingAnimated,
- NotificationIconContainer.IconState iconState) {
- if (iconState != null && expandingAnimated && mAmbientState.getScrollY() == 0
- && !mAmbientState.isOnKeyguard() && !iconState.isLastExpandIcon) {
- // We are expanding animated. Because we switch to a linear interpolation in this case,
- // the last icon may be stuck in between the shelf position and the notification
- // position, which looks pretty bad. We therefore optimize this case by applying a
- // shorter transition such that the icon is either fully in the notification or we clamp
- // it into the shelf if it's close enough.
- // We need to persist this, since after the expansion, the behavior should still be the
- // same.
- float position = mAmbientState.getIntrinsicPadding()
- + mHostLayoutController.getPositionInLinearLayout(view);
- int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
- if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart
- && view.getTranslationY() < position) {
- iconState.isLastExpandIcon = true;
- iconState.customTransformHeight = NO_VALUE;
- // Let's check if we're close enough to snap into the shelf
- boolean forceInShelf = mMaxLayoutHeight - getIntrinsicHeight() - position
- < getIntrinsicHeight();
- if (!forceInShelf) {
- // We are overlapping the shelf but not enough, so the icon needs to be
- // repositioned
- iconState.customTransformHeight = (int) (mMaxLayoutHeight
- - getIntrinsicHeight() - position);
- }
- }
- }
- }
-
private void updateIconPositioning(ExpandableView view, float iconTransitionAmount,
- float fullTransitionAmount, float iconTransformDistance, boolean scrolling,
boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
StatusBarIconView icon = view.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
if (iconState == null) {
return;
}
- boolean forceInShelf =
- iconState.isLastExpandIcon && !iconState.hasCustomTransformHeight();
boolean clampInShelf = iconTransitionAmount > 0.5f || isTargetClipped(view);
float clampedAmount = clampInShelf ? 1.0f : 0.0f;
if (iconTransitionAmount == clampedAmount) {
- iconState.noAnimations = (scrollingFast || expandingAnimated) && !forceInShelf;
- iconState.useFullTransitionAmount = iconState.noAnimations
- || (!ICON_ANMATIONS_WHILE_SCROLLING && iconTransitionAmount == 0.0f
- && scrolling);
- iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING
- && iconTransitionAmount == 0.0f && !mAmbientState.isExpansionChanging();
- iconState.translateContent = mMaxLayoutHeight - getTranslationY()
- - getIntrinsicHeight() > 0;
- }
- if (!forceInShelf && (scrollingFast || (expandingAnimated
- && iconState.useFullTransitionAmount && !ViewState.isAnimatingY(icon)))) {
+ iconState.noAnimations = (scrollingFast || expandingAnimated) && !isLastChild;
+ }
+ if (!isLastChild
+ && (scrollingFast || (expandingAnimated && !ViewState.isAnimatingY(icon)))) {
iconState.cancelAnimations(icon);
- iconState.useFullTransitionAmount = true;
iconState.noAnimations = true;
}
- if (iconState.hasCustomTransformHeight()) {
- iconState.useFullTransitionAmount = true;
- }
- if (iconState.isLastExpandIcon) {
- iconState.translateContent = false;
- }
float transitionAmount;
if (mAmbientState.isHiddenAtAll() && !view.isInShelf()) {
transitionAmount = mAmbientState.isFullyHidden() ? 1 : 0;
- } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING
- || iconState.useFullTransitionAmount
- || iconState.useLinearTransitionAmount) {
- transitionAmount = iconTransitionAmount;
} else {
transitionAmount = iconTransitionAmount;
iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount
&& !mNoAnimationsInThisFrame;
}
- iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
- || iconState.useFullTransitionAmount
- ? fullTransitionAmount
- : transitionAmount;
iconState.clampedAppearAmount = clampedAmount;
setIconTransformationAmount(view, transitionAmount, isLastChild);
}
@@ -706,8 +588,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
}
- private void setIconTransformationAmount(ExpandableView view,
- float transitionAmount,
+ private void setIconTransformationAmount(ExpandableView view, float transitionAmount,
boolean isLastChild) {
if (!(view instanceof ExpandableNotificationRow)) {
return;
@@ -718,32 +599,30 @@ public class NotificationShelf extends ActivatableNotificationView implements
if (iconState == null) {
return;
}
- iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
- boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
- if (isAppearing) {
- iconState.hidden = true;
- iconState.iconAppearAmount = 0.0f;
- }
iconState.alpha = transitionAmount;
+ boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
+ iconState.hidden = isAppearing
+ || (view instanceof ExpandableNotificationRow
+ && ((ExpandableNotificationRow) view).isLowPriority()
+ && mShelfIcons.hasMaxNumDot())
+ || (transitionAmount == 0.0f && !iconState.isAnimating(icon))
+ || row.isAboveShelf()
+ || row.showingPulsing()
+ || (!row.isInShelf() && isLastChild)
+ || row.getTranslationZ() > mAmbientState.getBaseZHeight();
+ iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;
+
// Fade in icons at shelf start
// This is important for conversation icons, which are badged and need x reset
iconState.xTranslation = mShelfIcons.getActualPaddingStart();
- boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
+ final boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
if (stayingInShelf) {
iconState.iconAppearAmount = 1.0f;
iconState.alpha = 1.0f;
- iconState.scaleX = 1.0f;
- iconState.scaleY = 1.0f;
iconState.hidden = false;
}
- if (row.isAboveShelf()
- || row.showingPulsing()
- || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
- || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
- iconState.hidden = true;
- }
int backgroundColor = getBackgroundColorWithoutTint();
int shelfColor = icon.getContrastedStaticDrawableColor(backgroundColor);
if (row.isShowingIcon() && shelfColor != StatusBarIconView.NO_COLOR) {
@@ -819,42 +698,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
return ret;
}
- private void setOpenedAmount(float openedAmount) {
- mNoAnimationsInThisFrame = openedAmount == 1.0f && mOpenedAmount == 0.0f;
- mOpenedAmount = openedAmount;
- if (!mAmbientState.isPanelFullWidth() || mAmbientState.isDozing()) {
- // We don't do a transformation at all, lets just assume we are fully opened
- openedAmount = 1.0f;
- }
- int start = mRelativeOffset;
- if (isLayoutRtl()) {
- start = getWidth() - start - mCollapsedIcons.getWidth();
- }
- int width = (int) NotificationUtils.interpolate(
- start + mCollapsedIcons.getFinalTranslationX(),
- mShelfIcons.getWidth(),
- FAST_OUT_SLOW_IN_REVERSE.getInterpolation(openedAmount));
- mShelfIcons.setActualLayoutWidth(width);
- boolean hasOverflow = mCollapsedIcons.hasOverflow();
- int collapsedPadding = mCollapsedIcons.getPaddingEnd();
- if (!hasOverflow) {
- // we have to ensure that adding the low priority notification won't lead to an
- // overflow
- collapsedPadding -= mCollapsedIcons.getNoOverflowExtraPadding();
- } else {
- // Partial overflow padding will fill enough space to add extra dots
- collapsedPadding -= mCollapsedIcons.getPartialOverflowExtraPadding();
- }
- float padding = NotificationUtils.interpolate(collapsedPadding,
- mShelfIcons.getPaddingEnd(),
- openedAmount);
- mShelfIcons.setActualPaddingEnd(padding);
- float paddingStart = NotificationUtils.interpolate(start,
- mShelfIcons.getPaddingStart(), openedAmount);
- mShelfIcons.setActualPaddingStart(paddingStart);
- mShelfIcons.setOpenedAmount(openedAmount);
- }
-
public void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
}
@@ -947,7 +790,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
private class ShelfState extends ExpandableViewState {
- private float openedAmount;
private boolean hasItemsInStableShelf;
@Override
@@ -957,7 +799,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
super.applyToView(view);
- setOpenedAmount(openedAmount);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
@@ -970,7 +811,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
super.animateTo(child, properties);
- setOpenedAmount(openedAmount);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 6ba52156c374..5219ecd1d83c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,16 +16,23 @@
package com.android.systemui.statusbar;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.text.format.DateFormat;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
@@ -81,6 +88,8 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
// Record the HISTORY_SIZE most recent states
private int mHistoryIndex = 0;
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
+ // This is used by InteractionJankMonitor to get callback from HWUI.
+ private View mView;
/**
* If any of the system bars is hidden.
@@ -236,6 +245,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
@Override
public void setDozeAmount(float dozeAmount, boolean animated) {
+ setAndInstrumentDozeAmount(null, dozeAmount, animated);
+ }
+
+ @Override
+ public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
if (animated && mDozeAmountTarget == dozeAmount) {
return;
@@ -244,6 +258,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
}
+ // We don't need a new attached view if we already have one.
+ if ((mView == null || !mView.isAttachedToWindow())
+ && (view != null && view.isAttachedToWindow())) {
+ mView = view;
+ }
mDozeAmountTarget = dozeAmount;
if (animated) {
startDozeAnimation();
@@ -261,6 +280,22 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
mDarkAnimator.setInterpolator(Interpolators.LINEAR);
mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
+ mDarkAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancelInteractionJankMonitor();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endInteractionJankMonitor();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ beginInteractionJankMonitor();
+ }
+ });
mDarkAnimator.start();
}
@@ -277,6 +312,24 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
}
+ private void beginInteractionJankMonitor() {
+ if (mView != null && mView.isAttachedToWindow()) {
+ InteractionJankMonitor.getInstance().begin(mView, getCujType());
+ }
+ }
+
+ private void endInteractionJankMonitor() {
+ InteractionJankMonitor.getInstance().end(getCujType());
+ }
+
+ private void cancelInteractionJankMonitor() {
+ InteractionJankMonitor.getInstance().cancel(getCujType());
+ }
+
+ private int getCujType() {
+ return mIsDozing ? CUJ_LOCKSCREEN_TRANSITION_TO_AOD : CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
+ }
+
@Override
public boolean goingToFullShade() {
return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index a2e07b289e9d..b6d6ed53b681 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.view.View;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -75,6 +76,15 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
*/
void setDozeAmount(float dozeAmount, boolean animated);
+ /**
+ * Changes the current doze amount, also starts the
+ * {@link com.android.internal.jank.InteractionJankMonitor InteractionJankMonitor} as possible.
+ *
+ * @param view An attached view, which will be used by InteractionJankMonitor.
+ * @param dozeAmount New doze/dark amount.
+ * @param animated If change should be animated or not. This will cancel current animations.
+ */
+ void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated);
/**
* Update the expanded state from {@link StatusBar}'s perspective
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index d08f9736adf6..85a1aed68559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -146,17 +146,19 @@ public class ActivityLaunchAnimator {
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
- private final float mNotificationCornerRadius;
- private float mCornerRadius;
private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
- public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
- mSourceNotification = sourceNofitication;
+ private final float mNotificationStartTopCornerRadius;
+ private final float mNotificationStartBottomCornerRadius;
+
+ AnimationRunner(ExpandableNotificationRow sourceNotification) {
+ mSourceNotification = sourceNotification;
mParams = new ExpandAnimationParameters();
mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
- mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
- mSourceNotification.getCurrentBottomRoundness());
+ mNotificationStartTopCornerRadius = mSourceNotification.getCurrentBackgroundRadiusTop();
+ mNotificationStartBottomCornerRadius =
+ mSourceNotification.getCurrentBackgroundRadiusBottom();
}
@Override
@@ -224,7 +226,10 @@ public class ActivityLaunchAnimator {
+ notificationHeight,
primary.position.y + primary.sourceContainerBounds.bottom,
progress);
- mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
+ mParams.topCornerRadius = MathUtils.lerp(mNotificationStartTopCornerRadius,
+ mWindowCornerRadius, progress);
+ mParams.bottomCornerRadius = MathUtils.lerp(
+ mNotificationStartBottomCornerRadius,
mWindowCornerRadius, progress);
applyParamsToWindow(primary);
applyParamsToNotification(mParams);
@@ -309,12 +314,13 @@ public class ActivityLaunchAnimator {
Matrix m = new Matrix();
m.postTranslate(0, (float) (mParams.top - app.position.y));
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
+ float cornerRadius = Math.min(mParams.topCornerRadius, mParams.bottomCornerRadius);
SurfaceParams params = new SurfaceParams.Builder(app.leash)
.withAlpha(1f)
.withMatrix(m)
.withWindowCrop(mWindowCrop)
.withLayer(app.prefixOrderIndex)
- .withCornerRadius(mCornerRadius)
+ .withCornerRadius(cornerRadius)
.withVisibility(true)
.build();
mSyncRtTransactionApplier.scheduleApply(params);
@@ -339,6 +345,8 @@ public class ActivityLaunchAnimator {
int bottom;
int startClipTopAmount;
int parentStartClipTopAmount;
+ float topCornerRadius;
+ float bottomCornerRadius;
public ExpandAnimationParameters() {
}
@@ -389,6 +397,14 @@ public class ActivityLaunchAnimator {
public float getStartTranslationZ() {
return startTranslationZ;
}
+
+ public float getTopCornerRadius() {
+ return topCornerRadius;
+ }
+
+ public float getBottomCornerRadius() {
+ return bottomCornerRadius;
+ }
}
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 004cf9968a77..b0d41f155713 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -218,13 +218,18 @@ class ConversationNotificationManager @Inject constructor(
})
}
+ private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
+ if (notification.flags and Notification.FLAG_ONLY_ALERT_ONCE != 0) {
+ false
+ } else {
+ val oldBuilder = Notification.Builder.recoverBuilder(context, notification)
+ Notification.areStyledNotificationsVisiblyDifferent(oldBuilder, newBuilder)
+ }
+
fun getUnreadCount(entry: NotificationEntry, recoveredBuilder: Notification.Builder): Int =
states.compute(entry.key) { _, state ->
val newCount = state?.run {
- val old = Notification.Builder.recoverBuilder(context, notification)
- val increment = Notification
- .areStyledNotificationsVisiblyDifferent(old, recoveredBuilder)
- if (increment) unreadCount + 1 else unreadCount
+ if (shouldIncrementUnread(recoveredBuilder)) unreadCount + 1 else unreadCount
} ?: 1
ConversationState(newCount, entry.sbn.notification)
}!!.unreadCount
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index dbd8580b751e..5f93f4807c73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -41,11 +41,11 @@ import android.app.NotificationChannel;
import android.app.NotificationManager.Policy;
import android.app.Person;
import android.app.RemoteInput;
-import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
@@ -534,8 +534,8 @@ public final class NotificationEntry extends ListEntry {
return false;
}
Bundle extras = mSbn.getNotification().extras;
- RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ Parcelable[] replyTexts =
+ extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
if (!ArrayUtils.isEmpty(replyTexts)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 18806effc545..5a3f48cec290 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -99,17 +99,11 @@ public class HighPriorityProvider {
private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
return !hasUserSetImportance(entry)
- && (isImportantOngoing(entry)
- || entry.getSbn().getNotification().hasMediaSession()
+ && (entry.getSbn().getNotification().hasMediaSession()
|| isPeopleNotification(entry)
|| isMessagingStyle(entry));
}
- private boolean isImportantOngoing(NotificationEntry entry) {
- return entry.getSbn().getNotification().isForegroundService()
- && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
- }
-
private boolean isMessagingStyle(NotificationEntry entry) {
return Notification.MessagingStyle.class.equals(
entry.getSbn().getNotification().getNotificationStyle());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 31d052d75998..18e5ead7fbb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -883,8 +883,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
private void applyBackgroundRoundness(float topRadius, float bottomRadius) {
- mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
- mBackgroundNormal.setRoundness(topRadius, bottomRadius);
+ mBackgroundDimmed.setRadius(topRadius, bottomRadius);
+ mBackgroundNormal.setRadius(topRadius, bottomRadius);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 50bbc38eddf3..3f7b8aff7c51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -104,7 +104,7 @@ public class ActivatableNotificationViewController
@Override
public boolean onTouch(View v, MotionEvent ev) {
- boolean result;
+ boolean result = false;
if (mBlockNextTouch) {
mBlockNextTouch = false;
return true;
@@ -112,16 +112,20 @@ public class ActivatableNotificationViewController
if (ev.getAction() == MotionEvent.ACTION_UP) {
mView.setLastActionUpTime(SystemClock.uptimeMillis());
}
- if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled()
- && mView.isInteractive()) {
+ // With a11y, just do nothing.
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ return false;
+ }
+ if (mNeedsDimming && mView.isInteractive()) {
if (mNeedsDimming && !mView.isDimmed()) {
// We're actually dimmed, but our content isn't dimmable,
// let's ensure we have a ripple
return false;
}
result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight());
- } else {
- return false;
+ } else if (ev.getAction() == MotionEvent.ACTION_UP) {
+ // If this is a false tap, capture the even so it doesn't result in a click.
+ return mFalsingManager.isFalseTap(true, 0.1);
}
return result;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0f23b770aacd..6cf5c303149c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -77,6 +77,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
@@ -166,7 +167,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private int mMaxSmallHeightLarge;
private int mMaxSmallHeightMedia;
private int mMaxExpandedHeight;
- private int mMaxCallHeight;
private int mIncreasedPaddingBetweenElements;
private int mNotificationLaunchHeight;
private boolean mMustStayOnScreen;
@@ -216,6 +216,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private NotificationGuts mGuts;
private NotificationEntry mEntry;
private String mAppName;
+ private FalsingManager mFalsingManager;
private FalsingCollector mFalsingCollector;
/**
@@ -347,6 +348,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
new SystemNotificationAsyncTask();
+ private float mTopRoundnessDuringExpandAnimation;
+ private float mBottomRoundnessDuringExpandAnimation;
+
/**
* Returns whether the given {@code statusBarNotification} is a system notification.
* <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC
@@ -687,7 +691,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// them a headerless design, then remove this hack.
smallHeight = mMaxSmallHeightLarge;
} else if (isCallLayout) {
- smallHeight = mMaxCallHeight;
+ smallHeight = mMaxExpandedHeight;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
smallHeight = mMaxSmallHeightLarge;
} else {
@@ -885,6 +889,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Other parts of the system may intercept and handle all the falsing.
+ // Otherwise, if we see motion and follow-on events, try to classify them as a tap.
+ if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ mFalsingManager.isFalseTap(true, 0.3);
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() != MotionEvent.ACTION_DOWN
|| !isChildInGroup() || isGroupExpanded()) {
@@ -1567,6 +1581,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
OnExpandClickListener onExpandClickListener,
NotificationMediaManager notificationMediaManager,
CoordinateOnClickListener onFeedbackClickListener,
+ FalsingManager falsingManager,
FalsingCollector falsingCollector,
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
@@ -1592,6 +1607,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mOnExpandClickListener = onExpandClickListener;
mMediaManager = notificationMediaManager;
setOnFeedbackClickListener(onFeedbackClickListener);
+ mFalsingManager = falsingManager;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -1621,8 +1637,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
R.dimen.notification_min_height_media);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
- mMaxCallHeight = NotificationUtils.getFontScaledHeight(mContext,
- R.dimen.call_notification_full_height);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_heads_up_height_legacy);
mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
@@ -2012,6 +2026,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return false;
}
+ @Override
+ public float getCurrentTopRoundness() {
+ if (mExpandAnimationRunning) {
+ return mTopRoundnessDuringExpandAnimation;
+ }
+
+ return super.getCurrentTopRoundness();
+ }
+
+ @Override
+ public float getCurrentBottomRoundness() {
+ if (mExpandAnimationRunning) {
+ return mBottomRoundnessDuringExpandAnimation;
+ }
+
+ return super.getCurrentBottomRoundness();
+ }
+
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
if (params == null) {
return;
@@ -2027,17 +2059,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
int top = params.getTop();
float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
int startClipTopAmount = params.getStartClipTopAmount();
+ int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
if (mNotificationParent != null) {
float parentY = mNotificationParent.getTranslationY();
top -= parentY;
mNotificationParent.setTranslationZ(translationZ);
+
+ // When the expanding notification is below its parent, the parent must be clipped
+ // exactly how it was clipped before the animation. When the expanding notification is
+ // on or above its parent (top <= 0), then the parent must be clipped exactly 'top'
+ // pixels to show the expanding notification, while still taking the decreasing
+ // notification clipTopAmount into consideration, so 'top + clipTopAmount'.
int parentStartClipTopAmount = params.getParentStartClipTopAmount();
- if (startClipTopAmount != 0) {
- int clipTopAmount = (int) MathUtils.lerp(parentStartClipTopAmount,
- parentStartClipTopAmount - startClipTopAmount,
- interpolation);
- mNotificationParent.setClipTopAmount(clipTopAmount);
- }
+ int parentClipTopAmount = Math.min(parentStartClipTopAmount,
+ top + clipTopAmount);
+ mNotificationParent.setClipTopAmount(parentClipTopAmount);
+
mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
float clipBottom = Math.max(params.getBottom(),
parentY + mNotificationParent.getActualHeight()
@@ -2046,12 +2083,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
int minimumHeightForClipping = (int) (clipBottom - clipTop);
mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
} else if (startClipTopAmount != 0) {
- int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
setClipTopAmount(clipTopAmount);
}
setTranslationY(top);
setActualHeight(params.getHeight());
+ mTopRoundnessDuringExpandAnimation = params.getTopCornerRadius() / mOutlineRadius;
+ mBottomRoundnessDuringExpandAnimation = params.getBottomCornerRadius() / mOutlineRadius;
+ invalidateOutline();
+
mBackgroundNormal.setExpandAnimationParams(params);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 0d0e97eae519..c9fcdac8e45f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -26,6 +26,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
@@ -78,6 +79,7 @@ public class ExpandableNotificationRowController implements NodeController {
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
+ private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -104,6 +106,7 @@ public class ExpandableNotificationRowController implements NodeController {
NotificationGutsManager notificationGutsManager,
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
OnUserInteractionCallback onUserInteractionCallback,
+ FalsingManager falsingManager,
FalsingCollector falsingCollector,
PeopleNotificationIdentifier peopleNotificationIdentifier,
Optional<BubblesManager> bubblesManagerOptional) {
@@ -125,6 +128,7 @@ public class ExpandableNotificationRowController implements NodeController {
mStatusBarStateController = statusBarStateController;
mNotificationGutsManager = notificationGutsManager;
mOnUserInteractionCallback = onUserInteractionCallback;
+ mFalsingManager = falsingManager;
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
mFalsingCollector = falsingCollector;
@@ -150,6 +154,7 @@ public class ExpandableNotificationRowController implements NodeController {
mOnExpandClickListener,
mMediaManager,
mOnFeedbackClickListener,
+ mFalsingManager,
mFalsingCollector,
mStatusBarStateController,
mPeopleNotificationIdentifier,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 85f556fa733c..3728388f63b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -27,7 +27,6 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -83,8 +82,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- if (!mCustomOutline && mCurrentTopRoundness == 0.0f
- && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners
+ if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
+ && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners
&& !mTopAmountRounded) {
int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
int left = Math.max(translation, 0);
@@ -135,10 +134,12 @@ public abstract class ExpandableOutlineView extends ExpandableView {
? mOutlineRadius : getCurrentBackgroundRadiusBottom();
if (topRoundness + bottomRoundness > height) {
float overShoot = topRoundness + bottomRoundness - height;
- topRoundness -= overShoot * mCurrentTopRoundness
- / (mCurrentTopRoundness + mCurrentBottomRoundness);
- bottomRoundness -= overShoot * mCurrentBottomRoundness
- / (mCurrentTopRoundness + mCurrentBottomRoundness);
+ float currentTopRoundness = getCurrentTopRoundness();
+ float currentBottomRoundness = getCurrentBottomRoundness();
+ topRoundness -= overShoot * currentTopRoundness
+ / (currentTopRoundness + currentBottomRoundness);
+ bottomRoundness -= overShoot * currentBottomRoundness
+ / (currentTopRoundness + currentBottomRoundness);
}
getRoundedRectPath(left, top, right, bottom, topRoundness, bottomRoundness, mTmpPath);
return mTmpPath;
@@ -267,7 +268,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
if (mTopAmountRounded) {
return mOutlineRadius;
}
- return mCurrentTopRoundness * mOutlineRadius;
+ return getCurrentTopRoundness() * mOutlineRadius;
}
public float getCurrentTopRoundness() {
@@ -278,8 +279,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
return mCurrentBottomRoundness;
}
- protected float getCurrentBackgroundRadiusBottom() {
- return mCurrentBottomRoundness * mOutlineRadius;
+ public float getCurrentBackgroundRadiusBottom() {
+ return getCurrentBottomRoundness() * mOutlineRadius;
}
@Override
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 ba03d01b20b0..73e080423d40 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
@@ -69,7 +69,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
private float mContentTranslation;
protected boolean mLastInSection;
protected boolean mFirstInSection;
- boolean mIsBeingSwiped;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -174,14 +173,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
return false;
}
- public void setIsBeingSwiped(boolean swiped) {
- mIsBeingSwiped = swiped;
- }
-
- public boolean isBeingSwiped() {
- return mIsBeingSwiped;
- }
-
public boolean isHeadsUpAnimatingAway() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 62d596b60a89..95885633a3e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -224,10 +224,9 @@ public class NotificationBackgroundView extends View {
}
/**
- * Sets the current top and bottom roundness amounts for this background, between 0.0 (not
- * rounded) and 1.0 (maximally rounded).
+ * Sets the current top and bottom radius for this background.
*/
- public void setRoundness(float topRoundness, float bottomRoundness) {
+ public void setRadius(float topRoundness, float bottomRoundness) {
if (topRoundness == mCornerRadii[0] && bottomRoundness == mCornerRadii[4]) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index fdd8f347c248..58b87cd2f492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -27,8 +27,10 @@ import android.app.Notification;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.CancellationSignal;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.View;
@@ -768,10 +770,26 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return mReInflateFlags;
}
+ void updateApplicationInfo(StatusBarNotification sbn) {
+ String packageName = sbn.getPackageName();
+ int userId = UserHandle.getUserId(sbn.getUid());
+ final ApplicationInfo appInfo;
+ try {
+ // This method has an internal cache, so we don't need to add our own caching here.
+ appInfo = mContext.getPackageManager().getApplicationInfoAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ Notification.addFieldsFromContext(appInfo, sbn.getNotification());
+ }
+
@Override
protected InflationProgress doInBackground(Void... params) {
try {
final StatusBarNotification sbn = mEntry.getSbn();
+ // Ensure the ApplicationInfo is updated before a builder is recovered.
+ updateApplicationInfo(sbn);
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
sbn.getNotification());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index d3065aa36a5f..55a27b2b0052 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -22,8 +22,6 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -32,7 +30,6 @@ import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
@@ -44,7 +41,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -349,7 +345,9 @@ public class NotificationContentView extends FrameLayout {
invalidateOutline();
selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
mForceSelectNextLayout = false;
- updateExpandButtons(mExpandable);
+ // TODO(b/182314698): move this to onMeasure. This requires switching to getMeasuredHeight,
+ // and also requires revisiting all of the logic called earlier in this method.
+ updateExpandButtonsDuringLayout(mExpandable, true /* duringLayout */);
}
@Override
@@ -1272,23 +1270,6 @@ public class NotificationContentView extends FrameLayout {
}
}
if (hasRemoteInput) {
- int color = entry.getSbn().getNotification().color;
- if (color == Notification.COLOR_DEFAULT) {
- color = mContext.getColor(R.color.default_remote_input_background);
- }
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_tintNotificationsWithTheme)) {
- Resources.Theme theme = new ContextThemeWrapper(mContext,
- com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
- TypedArray ta = theme.obtainStyledAttributes(
- new int[]{com.android.internal.R.attr.colorAccent});
- color = ta.getColor(0, color);
- ta.recycle();
- }
- existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
- mContext.getColor(R.color.remote_input_text_enabled),
- mContext.getColor(R.color.remote_input_hint)));
-
existing.setWrapper(wrapper);
existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
@@ -1310,6 +1291,10 @@ public class NotificationContentView extends FrameLayout {
}
}
}
+
+ if (existing != null && entry.getSbn().getNotification().isColorized()) {
+ existing.overrideBackgroundTintColor(entry.getSbn().getNotification().color);
+ }
return existing;
}
return null;
@@ -1344,9 +1329,7 @@ public class NotificationContentView extends FrameLayout {
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- LinearLayout actionContainerLayout =
- layout.findViewById(com.android.internal.R.id.actions_container_layout);
- if (bubbleButton == null || actionContainer == null || actionContainerLayout == null) {
+ if (bubbleButton == null || actionContainer == null) {
return;
}
boolean isPersonWithShortcut =
@@ -1589,6 +1572,10 @@ public class NotificationContentView extends FrameLayout {
}
public void updateExpandButtons(boolean expandable) {
+ updateExpandButtonsDuringLayout(expandable, false /* duringLayout */);
+ }
+
+ private void updateExpandButtonsDuringLayout(boolean expandable, boolean duringLayout) {
mExpandable = expandable;
// if the expanded child has the same height as the collapsed one we hide it.
if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
@@ -1602,14 +1589,15 @@ public class NotificationContentView extends FrameLayout {
expandable = false;
}
}
+ boolean requestLayout = duringLayout && mIsContentExpandable != expandable;
if (mExpandedChild != null) {
- mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
+ mExpandedWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
}
if (mContractedChild != null) {
- mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
+ mContractedWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
}
if (mHeadsUpChild != null) {
- mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
+ mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
}
mIsContentExpandable = expandable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 4541ebf4c4f2..12e94cbc1ab9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -34,7 +34,7 @@ class NotificationCallTemplateViewWrapper constructor(
) : NotificationTemplateViewWrapper(ctx, view, row) {
private val minHeightWithActions: Int =
- NotificationUtils.getFontScaledHeight(ctx, R.dimen.call_notification_full_height)
+ NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
private val callLayout: CallLayout = view as CallLayout
private lateinit var conversationIconView: CachingIconView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index fb0fdcccd4b1..383bb7e41a91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -147,8 +147,11 @@ class NotificationConversationTemplateViewWrapper constructor(
override fun setRemoteInputVisible(visible: Boolean) =
conversationLayout.showHistoricMessages(visible)
- override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) =
- conversationLayout.updateExpandability(expandable, onClickListener)
+ override fun updateExpandability(
+ expandable: Boolean,
+ onClickListener: View.OnClickListener,
+ requestLayout: Boolean
+ ) = conversationLayout.updateExpandability(expandable, onClickListener)
override fun disallowSingleClick(x: Float, y: Float): Boolean {
val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index bdafd232167d..5a55545351d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -261,7 +261,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
@Override
- public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+ public void updateExpandability(boolean expandable, View.OnClickListener onClickListener,
+ boolean requestLayout) {
mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
mExpandButton.setOnClickListener(expandable ? onClickListener : null);
if (mAltExpandTarget != null) {
@@ -273,6 +274,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
if (mNotificationHeader != null) {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
+ // Unfortunately, the NotificationContentView has to layout its children in order to
+ // determine their heights, and that affects the button visibility. If that happens
+ // (thankfully it is rare) then we need to request layout of the expand button's parent
+ // in order to ensure it gets laid out correctly.
+ if (requestLayout) {
+ mExpandButton.getParent().requestLayout();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 9ced12d32d27..3a7b4618ad22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -291,8 +291,10 @@ public abstract class NotificationViewWrapper implements TransformableView {
*
* @param expandable should this view be expandable
* @param onClickListener the listener to invoke when the expand affordance is clicked on
+ * @param requestLayout the expandability changed during onLayout, so a requestLayout required
*/
- public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
+ public void updateExpandability(boolean expandable, View.OnClickListener onClickListener,
+ boolean requestLayout) {}
/** Set the expanded state on the view wrapper */
public void setExpanded(boolean expanded) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index d8ee102064e1..1d307364d661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -34,7 +34,6 @@ import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -325,7 +324,7 @@ public class NotificationChildrenContainer extends ViewGroup {
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
notification.getNotification());
- RemoteViews header = builder.makeNotificationHeader();
+ RemoteViews header = builder.makeNotificationGroupHeader();
if (mNotificationHeader == null) {
mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
mNotificationHeader.findViewById(com.android.internal.R.id.expand_button)
@@ -338,6 +337,7 @@ public class NotificationChildrenContainer extends ViewGroup {
} else {
header.reapply(getContext(), mNotificationHeader);
}
+ mNotificationHeaderWrapper.setExpanded(mChildrenExpanded);
mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
NotificationHeaderViewWrapper headerWrapper =
@@ -638,10 +638,6 @@ public class NotificationChildrenContainer extends ViewGroup {
childState.location = parentState.location;
childState.inShelf = parentState.inShelf;
yPosition += intrinsicHeight;
- if (child.isExpandAnimationRunning()) {
- launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
- }
-
}
if (mOverflowNumber != null) {
ExpandableNotificationRow overflowView = mAttachedChildren.get(Math.min(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 45f5b3136b9b..b1ac12e84fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack;
import android.content.res.Resources;
import android.util.MathUtils;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -47,6 +48,10 @@ public class NotificationRoundnessManager {
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
+ private ExpandableView mSwipedView = null;
+ private ExpandableView mViewBeforeSwipedView = null;
+ private ExpandableView mViewAfterSwipedView = null;
+
@Inject
NotificationRoundnessManager(
KeyguardBypassController keyguardBypassController,
@@ -68,6 +73,11 @@ public class NotificationRoundnessManager {
boolean updateViewWithoutCallback(ExpandableView view,
boolean animate) {
+ if (view == null
+ || view == mViewBeforeSwipedView
+ || view == mViewAfterSwipedView) {
+ return false;
+ }
float topRoundness = getRoundness(view, true /* top */);
float bottomRoundness = getRoundness(view, false /* top */);
boolean topChanged = view.setTopRoundness(topRoundness, animate);
@@ -105,9 +115,60 @@ public class NotificationRoundnessManager {
return false;
}
+ void setViewsAffectedBySwipe(
+ ExpandableView viewBefore,
+ ExpandableView viewSwiped,
+ ExpandableView viewAfter,
+ boolean cornerAnimationsEnabled) {
+ if (!cornerAnimationsEnabled) {
+ return;
+ }
+ final boolean animate = true;
+
+ ExpandableView oldViewBefore = mViewBeforeSwipedView;
+ mViewBeforeSwipedView = viewBefore;
+ if (oldViewBefore != null) {
+ final float bottomRoundness = getRoundness(oldViewBefore, false /* top */);
+ oldViewBefore.setBottomRoundness(bottomRoundness, animate);
+ }
+ if (viewBefore != null) {
+ viewBefore.setBottomRoundness(1f, animate);
+ }
+
+ ExpandableView oldSwipedview = mSwipedView;
+ mSwipedView = viewSwiped;
+ if (oldSwipedview != null) {
+ final float bottomRoundness = getRoundness(oldSwipedview, false /* top */);
+ final float topRoundness = getRoundness(oldSwipedview, true /* top */);
+ oldSwipedview.setTopRoundness(topRoundness, animate);
+ oldSwipedview.setBottomRoundness(bottomRoundness, animate);
+ }
+ if (viewSwiped != null) {
+ viewSwiped.setTopRoundness(1f, animate);
+ viewSwiped.setBottomRoundness(1f, animate);
+ }
+
+ ExpandableView oldViewAfter = mViewAfterSwipedView;
+ mViewAfterSwipedView = viewAfter;
+ if (oldViewAfter != null) {
+ final float topRoundness = getRoundness(oldViewAfter, true /* top */);
+ oldViewAfter.setTopRoundness(topRoundness, animate);
+ }
+ if (viewAfter != null) {
+ viewAfter.setTopRoundness(1f, animate);
+ }
+ }
+
private float getRoundness(ExpandableView view, boolean top) {
+ if (view == null) {
+ return 0f;
+ }
+ if (view == mViewBeforeSwipedView
+ || view == mSwipedView
+ || view == mViewAfterSwipedView) {
+ return 1f;
+ }
if ((view.isPinned()
- || view.isBeingSwiped()
|| (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
return 1.0f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 6c8cdf67d974..b06f7d25db16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -318,9 +318,6 @@ class NotificationSectionsManager @Inject internal constructor(
(child == null || row != null && nextBucket != row.entry.bucket)
if (isSectionBoundary && showHeaders) {
when (nextBucket) {
- BUCKET_HEADS_UP -> incomingState?.targetPosition = i + 1
- BUCKET_PEOPLE -> peopleState?.targetPosition = i + 1
- BUCKET_ALERTING -> alertingState?.targetPosition = i + 1
BUCKET_SILENT -> gentleState?.targetPosition = i + 1
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 417ff5e9aed0..970efd5cbfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1025,8 +1025,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean clip = clipStart > start && clipStart < end
|| clipEnd >= start && clipEnd <= end;
clip &= !(first && mScrollAdapter.isScrolledToTop());
- child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
- : ExpandableView.NO_ROUNDNESS);
+ child.setDistanceToTopRoundness(ExpandableView.NO_ROUNDNESS);
first = false;
}
}
@@ -2292,9 +2291,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
ExpandableView child = (ExpandableView) getChildAt(i);
if (child.getVisibility() != View.GONE
&& !(child instanceof StackScrollerDecorView)
- && child != mShelf
- && (mSwipeHelper.getSwipedView() != child
- || !child.getResources().getBoolean(R.bool.flag_notif_updates))) {
+ && child != mShelf) {
children.add(child);
}
}
@@ -4993,28 +4990,48 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSwipedOutViews.add(v);
}
- void onSwipeBegin(View v) {
- if (v instanceof ExpandableView) {
- ExpandableView ev = (ExpandableView) v;
- ev.setIsBeingSwiped(true);
- mController.getNoticationRoundessManager()
- .updateViewWithoutCallback(ev, true /* animate */);
+ void onSwipeBegin(View viewSwiped) {
+ if (!(viewSwiped instanceof ExpandableNotificationRow)) {
+ return;
}
- requestDisallowInterceptTouchEvent(true);
+ final int indexOfSwipedView = indexOfChild(viewSwiped);
+ if (indexOfSwipedView < 0) {
+ return;
+ }
+ mSectionsManager.updateFirstAndLastViewsForAllSections(
+ mSections, getChildrenWithBackground());
+ View viewBefore = null;
+ if (indexOfSwipedView > 0) {
+ viewBefore = getChildAt(indexOfSwipedView - 1);
+ if (mSectionsManager.beginsSection(viewSwiped, viewBefore)) {
+ viewBefore = null;
+ }
+ }
+ View viewAfter = null;
+ if (indexOfSwipedView < getChildCount()) {
+ viewAfter = getChildAt(indexOfSwipedView + 1);
+ if (mSectionsManager.beginsSection(viewAfter, viewSwiped)) {
+ viewAfter = null;
+ }
+ }
+ mController.getNoticationRoundessManager()
+ .setViewsAffectedBySwipe((ExpandableView) viewBefore,
+ (ExpandableView) viewSwiped,
+ (ExpandableView) viewAfter,
+ getResources().getBoolean(R.bool.flag_notif_updates));
+
updateFirstAndLastBackgroundViews();
+ requestDisallowInterceptTouchEvent(true);
updateContinuousShadowDrawing();
updateContinuousBackgroundDrawing();
requestChildrenUpdate();
}
- void onSwipeEnd(View v) {
- if (v instanceof ExpandableView) {
- ExpandableView ev = (ExpandableView) v;
- ev.setIsBeingSwiped(false);
- mController.getNoticationRoundessManager()
- .updateViewWithoutCallback(ev, true /* animate */);
- }
+ void onSwipeEnd() {
updateFirstAndLastBackgroundViews();
+ mController.getNoticationRoundessManager()
+ .setViewsAffectedBySwipe(null, null, null,
+ getResources().getBoolean(R.bool.flag_notif_updates));
}
void setTopHeadsUpEntry(NotificationEntry topEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 399702869d70..7baad1cc265e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -399,7 +399,7 @@ public class NotificationStackScrollLayoutController {
if (mView.getDismissAllInProgress()) {
return;
}
- mView.onSwipeEnd(view);
+ mView.onSwipeEnd();
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
@@ -459,7 +459,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onChildSnappedBack(View animView, float targetLeft) {
- mView.onSwipeEnd(animView);
+ mView.onSwipeEnd();
if (animView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
if (row.isPinned() && !canChildBeDismissed(row)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 5d2203b57991..d7a98bdf2715 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -28,14 +28,12 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
/**
@@ -156,7 +154,7 @@ public class StackScrollAlgorithm {
private void updateClipping(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
- + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
+ + ambientState.getStackTranslation()
: 0;
float clipStart = 0;
int childCount = algorithmState.visibleChildren.size();
@@ -329,9 +327,6 @@ public class StackScrollAlgorithm {
childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
float inset = ambientState.getTopPadding() + ambientState.getStackTranslation()
+ ambientState.getSectionPadding();
- if (i <= algorithmState.getIndexOfExpandingNotification()) {
- inset += ambientState.getExpandAnimationTopChange();
- }
if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
// Even if we're not scrolled away we're in view and we're also not in the
// shelf. We can relax the constraints and let us scroll off the top!
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index e6731e6b8a34..c60bbc5f547f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -166,6 +166,7 @@ public class BarTransitions {
private int mGradientAlpha;
private int mColor;
+ private float mOverrideAlpha = 1f;
private PorterDuffColorFilter mTintFilter;
private Paint mPaint = new Paint();
@@ -195,6 +196,23 @@ public class BarTransitions {
mFrame = frame;
}
+ public void setOverrideAlpha(float overrideAlpha) {
+ mOverrideAlpha = overrideAlpha;
+ invalidateSelf();
+ }
+
+ public float getOverrideAlpha() {
+ return mOverrideAlpha;
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ public Rect getFrame() {
+ return mFrame;
+ }
+
@Override
public void setAlpha(int alpha) {
// noop
@@ -296,11 +314,13 @@ public class BarTransitions {
mGradient.setAlpha(mGradientAlpha);
mGradient.draw(canvas);
}
+
if (Color.alpha(mColor) > 0) {
mPaint.setColor(mColor);
if (mTintFilter != null) {
mPaint.setColorFilter(mTintFilter);
}
+ mPaint.setAlpha((int) (Color.alpha(mColor) * mOverrideAlpha));
if (mFrame != null) {
canvas.drawRect(mFrame, mPaint);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 85d8df8e6057..c23f1ad6f9c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -148,6 +148,11 @@ public class DozeParameters implements TunerService.Tunable,
return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
}
+ public int getQuickPickupAodDuration() {
+ return getInt("doze.gesture.quickpickup.duration",
+ R.integer.doze_quick_pickup_aod_duration);
+ }
+
/**
* For how long a wallpaper can be visible in AoD before it fades aways.
* @return duration in millis.
@@ -175,6 +180,10 @@ public class DozeParameters implements TunerService.Tunable,
return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
}
+ public boolean isQuickPickupEnabled() {
+ return mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
+ }
+
/**
* Some screens need to be completely black before changing the display power mode,
* unexpected behavior might happen if this parameter isn't respected.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9561851ab28b..6b144c652c56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
import android.util.Property;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.animation.Interpolator;
@@ -35,6 +36,7 @@ import androidx.collection.ArrayMap;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
@@ -155,7 +157,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private int mCannedAnimationStartIndex = -1;
private int mSpeedBumpIndex = -1;
private int mIconSize;
- private float mOpenedAmount = 0.0f;
private boolean mDisallowNextAnimation;
private boolean mAnimationsEnabled = true;
private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
@@ -169,6 +170,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private Rect mIsolatedIconLocation;
private int[] mAbsolutePosition = new int[2];
private View mIsolatedIconForAnimation;
+ private int mThemedTextColorPrimary;
public NotificationIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -180,6 +182,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
mStaticDotDiameter = 2 * mStaticDotRadius;
+ final Context themedContext = new ContextThemeWrapper(getContext(),
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+ mThemedTextColorPrimary = Utils.getColorAttr(themedContext,
+ com.android.internal.R.attr.textColorPrimary).getDefaultColor();
}
@Override
@@ -349,6 +355,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}
+ public boolean hasMaxNumDot() {
+ return mNumDots >= MAX_DOTS;
+ }
+
private boolean areAnimationsEnabled(StatusBarIconView icon) {
return mAnimationsEnabled || icon == mIsolatedIcon;
}
@@ -391,7 +401,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float overflowStart = getMaxOverflowStart();
mVisualOverflowStart = 0;
mFirstVisibleIconState = null;
- boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
IconState iconState = mIconStates.get(view);
@@ -410,10 +419,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
: 1f;
- if (mOpenedAmount != 0.0f) {
- noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
- }
- iconState.visibleState = StatusBarIconView.STATE_ICON;
+ iconState.visibleState = iconState.hidden
+ ? StatusBarIconView.STATE_HIDDEN
+ : StatusBarIconView.STATE_ICON;
boolean isOverflowing =
(translationX > (noOverflowAfter ? layoutEnd - mIconSize
@@ -598,10 +606,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mSpeedBumpIndex = speedBumpIndex;
}
- public void setOpenedAmount(float expandAmount) {
- mOpenedAmount = expandAmount;
- }
-
public boolean hasOverflow() {
return mNumDots > 0;
}
@@ -706,13 +710,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
public boolean justAdded = true;
private boolean justReplaced;
public boolean needsCannedAnimation;
- public boolean useFullTransitionAmount;
- public boolean useLinearTransitionAmount;
- public boolean translateContent;
public int iconColor = StatusBarIconView.NO_COLOR;
public boolean noAnimations;
- public boolean isLastExpandIcon;
- public int customTransformHeight = NO_VALUE;
private final View mView;
private final Consumer<Property> mCannedAnimationEndListener;
@@ -734,8 +733,15 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
StatusBarIconView icon = (StatusBarIconView) view;
boolean animate = false;
AnimationProperties animationProperties = null;
- boolean animationsAllowed = areAnimationsEnabled(icon) && !mDisallowNextAnimation
- && !noAnimations;
+ final boolean isLowPriorityIconChange =
+ (visibleState == StatusBarIconView.STATE_HIDDEN
+ && icon.getVisibleState() == StatusBarIconView.STATE_DOT)
+ || (visibleState == StatusBarIconView.STATE_DOT
+ && icon.getVisibleState() == StatusBarIconView.STATE_HIDDEN);
+ final boolean animationsAllowed = areAnimationsEnabled(icon)
+ && !mDisallowNextAnimation
+ && !noAnimations
+ && !isLowPriorityIconChange;
if (animationsAllowed) {
if (justAdded || justReplaced) {
super.applyToView(icon);
@@ -807,7 +813,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}
icon.setVisibleState(visibleState, animationsAllowed);
- icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
+ icon.setIconColor(mThemedTextColorPrimary,
+ needsCannedAnimation && animationsAllowed);
if (animate) {
animateTo(icon, animationProperties);
} else {
@@ -822,10 +829,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
needsCannedAnimation = false;
}
- public boolean hasCustomTransformHeight() {
- return isLastExpandIcon && customTransformHeight != NO_VALUE;
- }
-
@Override
public void initFrom(View view) {
super.initFrom(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 19b98953325f..cd712044a60e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -439,7 +439,6 @@ public class NotificationPanelViewController extends PanelViewController {
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mUserSetupComplete;
private int mQsNotificationTopPadding;
- private float mExpandOffset;
private boolean mHideIconsDuringNotificationLaunch = true;
private int mStackScrollerMeasuringPass;
private ArrayList<Consumer<ExpandableNotificationRow>>
@@ -576,8 +575,8 @@ public class NotificationPanelViewController extends PanelViewController {
FeatureFlags featureFlags) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
- ambientState);
+ statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
+ statusBarTouchableRegionManager, ambientState);
mView = view;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
@@ -1349,7 +1348,6 @@ public class NotificationPanelViewController extends PanelViewController {
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
-
private boolean onQsIntercept(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
@@ -2444,8 +2442,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
startHeight = -mQs.getQsMinExpansionHeight();
}
- float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
- + mExpandOffset;
+ float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount));
return Math.min(0, translation);
}
@@ -3130,7 +3127,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
final float dozeAmount = dozing ? 1 : 0;
- mStatusBarStateController.setDozeAmount(dozeAmount, animate);
+ mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate);
}
public void setPulsing(boolean pulsing) {
@@ -3185,16 +3182,16 @@ public class NotificationPanelViewController extends PanelViewController {
}
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
- mExpandOffset = params != null ? params.getTopChange() : 0;
- updateQsExpansion();
- if (params != null) {
- boolean hideIcons = params.getProgress(
- ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
- if (hideIcons != mHideIconsDuringNotificationLaunch) {
- mHideIconsDuringNotificationLaunch = hideIcons;
- if (!hideIcons) {
- mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
- }
+ if (params == null) {
+ return;
+ }
+
+ boolean hideIcons = params.getProgress(
+ ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+ if (hideIcons != mHideIconsDuringNotificationLaunch) {
+ mHideIconsDuringNotificationLaunch = hideIcons;
+ if (!hideIcons) {
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
}
}
}
@@ -3389,6 +3386,9 @@ public class NotificationPanelViewController extends PanelViewController {
return new TouchHandler() {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) {
+ return true;
+ }
if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
return false;
}
@@ -3416,6 +3416,12 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public boolean onTouch(View v, MotionEvent event) {
+ final boolean showingOrAnimatingAltAuth =
+ mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating();
+ if (showingOrAnimatingAltAuth) {
+ mStatusBarKeyguardViewManager.resetAlternateAuth();
+ }
+
if (mBlockTouches || (mQsFullyExpanded && mQs != null
&& mQs.disallowPanelTouches())) {
return false;
@@ -3467,7 +3473,7 @@ public class NotificationPanelViewController extends PanelViewController {
handled = true;
}
handled |= super.onTouch(v, event);
- return !mDozing || mPulsing || handled;
+ return !mDozing || mPulsing || handled || showingOrAnimatingAltAuth;
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
index 50c8e2e0d710..66df936dd556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
@@ -64,7 +64,7 @@ public class NotificationTapHelper {
mTrackTouch = event.getY() <= maxTouchableHeight;
break;
case MotionEvent.ACTION_MOVE:
- if (mTrackTouch && mFalsingManager.isFalseTap(false)) {
+ if (mTrackTouch && mFalsingManager.isFalseTap(false, 0)) {
makeInactive();
mTrackTouch = false;
}
@@ -78,10 +78,10 @@ public class NotificationTapHelper {
// 1) See if we have confidence that we can activate after a single tap.
// 2) Else, see if it looks like a tap at all and check for a double-tap.
- if (!mFalsingManager.isFalseTap(true)) {
+ if (!mFalsingManager.isFalseTap(true, 0)) {
makeInactive();
return mDoubleTapListener.onDoubleTap();
- } else if (!mFalsingManager.isFalseTap(false)) {
+ } else if (!mFalsingManager.isFalseTap(false, 0)) {
if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 0c9ed661925c..1cb0be0efc90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -89,30 +89,22 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
@Override
protected void dispatchDraw(Canvas canvas) {
- // Invert the order of the scroll view and user switcher such that the notifications receive
- // touches first but the panel gets drawn above.
mDrawingOrderedChildren.clear();
mLayoutDrawingOrder.clear();
if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
mDrawingOrderedChildren.add(mKeyguardStatusBar);
mLayoutDrawingOrder.add(mKeyguardStatusBar);
}
- if (mStackScroller.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mStackScroller);
- mLayoutDrawingOrder.add(mStackScroller);
- }
if (mQsFrame.getVisibility() == View.VISIBLE) {
mDrawingOrderedChildren.add(mQsFrame);
mLayoutDrawingOrder.add(mQsFrame);
}
-
- if (mHasViewsAboveShelf) {
- // StackScroller needs to be on top
- mDrawingOrderedChildren.remove(mStackScroller);
+ if (mStackScroller.getVisibility() == View.VISIBLE) {
mDrawingOrderedChildren.add(mStackScroller);
+ mLayoutDrawingOrder.add(mStackScroller);
}
- // Let's now find the order that the view has when drawing regulary by sorting
+ // Let's now find the order that the view has when drawing regularly by sorting
mLayoutDrawingOrder.sort(mIndexComparator);
super.dispatchDraw(canvas);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index b6ed3e50ed7e..ed4f32492c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -167,6 +167,7 @@ public abstract class PanelViewController {
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
private final PanelView mView;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
protected final Resources mResources;
protected final KeyguardStateController mKeyguardStateController;
protected final SysuiStatusBarStateController mStatusBarStateController;
@@ -235,12 +236,14 @@ public abstract class PanelViewController {
FalsingManager falsingManager, DozeLog dozeLog,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
LatencyTracker latencyTracker,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
AmbientState ambientState) {
mAmbientState = ambientState;
mView = view;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -450,7 +453,8 @@ public abstract class PanelViewController {
// We need to collapse the panel since we peeked to the small height.
mView.postOnAnimation(mPostCollapseRunnable);
}
- } else if (!mStatusBar.isBouncerShowing()) {
+ } else if (!mStatusBar.isBouncerShowing()
+ && !mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d53724159244..f1405dea1294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -675,8 +675,7 @@ public class PhoneStatusBarPolicy
mIconController.setIconVisibility(mSlotCamera, showCamera);
mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
- if (mPrivacyItemController.getAllIndicatorsAvailable()
- || mPrivacyItemController.getLocationAvailable()) {
+ if (mPrivacyItemController.getLocationAvailable()) {
mIconController.setIconVisibility(mSlotLocation, showLocation);
}
mPrivacyLogger.logStatusBarIconsVisible(showCamera, showMicrophone, showLocation);
@@ -684,8 +683,7 @@ public class PhoneStatusBarPolicy
@Override
public void onLocationActiveChanged(boolean active) {
- if (!mPrivacyItemController.getAllIndicatorsAvailable()
- && !mPrivacyItemController.getLocationAvailable()) {
+ if (!mPrivacyItemController.getLocationAvailable()) {
updateLocationFromController();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 48c97a2fd79c..b82863e75fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -88,6 +88,17 @@ public enum ScrimState {
}
},
+ AUTH_SCRIMMED {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mFrontTint = Color.BLACK;
+
+ mBehindAlpha = 0f;
+ mFrontAlpha = .66f;
+ mBubbleAlpha = 0f;
+ }
+ },
+
/**
* Showing password challenge on the keyguard.
*/
@@ -140,11 +151,13 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
+ final boolean quickPickupEnabled = mDozeParameters.isQuickPickupEnabled();
final boolean isDocked = mDockManager.isDocked();
mBlankScreen = mDisplayRequiresBlanking;
mFrontTint = Color.BLACK;
- mFrontAlpha = (alwaysOnEnabled || isDocked) ? mAodFrontScrimAlpha : 1f;
+ mFrontAlpha = (alwaysOnEnabled || isDocked || quickPickupEnabled)
+ ? mAodFrontScrimAlpha : 1f;
mBehindTint = Color.BLACK;
mBehindAlpha = ScrimController.TRANSPARENT;
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 0807f8aa5607..5045e95dd53f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -95,6 +95,7 @@ import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -661,6 +662,16 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
}
};
+
+
+ private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
+ new FalsingManager.FalsingBeliefListener() {
+ @Override
+ public void onFalse() {
+ mStatusBarKeyguardViewManager.reset(true);
+ }
+ };
+
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private HeadsUpAppearanceController mHeadsUpAppearanceController;
@@ -999,6 +1010,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mInitController.addPostInitTask(
() -> setUpDisableFlags(disabledFlags1, disabledFlags2));
+ mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
+
mPluginManager.addPluginListener(
new PluginListener<OverlayPlugin>() {
private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
@@ -4071,7 +4084,9 @@ public class StatusBar extends SystemUI implements DemoMode,
private @Nullable Intent getEmergencyActionIntent() {
Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
PackageManager pm = mContext.getPackageManager();
- ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
+ List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ ResolveInfo resolveInfo = getTopEmergencySosInfo(emergencyActivities);
if (resolveInfo == null) {
Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
return null;
@@ -4082,6 +4097,34 @@ public class StatusBar extends SystemUI implements DemoMode,
return emergencyIntent;
}
+ /**
+ * Select and return the "best" ResolveInfo for Emergency SOS Activity.
+ */
+ private @Nullable ResolveInfo getTopEmergencySosInfo(List<ResolveInfo> emergencyActivities) {
+ // No matched activity.
+ if (emergencyActivities == null || emergencyActivities.isEmpty()) {
+ return null;
+ }
+
+ // Of multiple matched Activities, give preference to the pre-set package name.
+ String preferredAppPackageName =
+ mContext.getString(R.string.config_preferredEmergencySosPackage);
+
+ // If there is no preferred app, then return first match.
+ if (TextUtils.isEmpty(preferredAppPackageName)) {
+ return emergencyActivities.get(0);
+ }
+
+ for (ResolveInfo emergencyInfo: emergencyActivities) {
+ // If activity is from the preferred app, use it.
+ if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) {
+ return emergencyInfo;
+ }
+ }
+ // No matching activity: return first match
+ return emergencyActivities.get(0);
+ }
+
boolean isCameraAllowedByAdmin() {
if (mDevicePolicyManager.getCameraDisabled(null,
mLockscreenUserManager.getCurrentUserId())) {
@@ -4116,7 +4159,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
@VisibleForTesting
- void updateScrimController() {
+ public void updateScrimController() {
Trace.beginSection("StatusBar#updateScrimController");
// We don't want to end up in KEYGUARD state when we're unlocking with
@@ -4132,7 +4175,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
- if (mBouncerShowing) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternativeAuth()) {
+ mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+ } else if (mBouncerShowing) {
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5083e330f9a0..81e24cc25aa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -38,6 +38,7 @@ import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
@@ -76,7 +77,7 @@ import javax.inject.Inject;
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
* via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
* which is in turn, reported to this class by the current
- * {@link com.android.keyguard.KeyguardViewBase}.
+ * {@link com.android.keyguard.KeyguardViewController}.
*/
@SysUISingleton
public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
@@ -199,6 +200,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private KeyguardBypassController mBypassController;
+ @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@@ -271,6 +273,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
registerListeners();
}
+ public void setAlternateAuthInterceptor(@Nullable AlternateAuthInterceptor authInterceptor) {
+ mAlternateAuthInterceptor = authInterceptor;
+ }
+
private void registerListeners() {
mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
mStatusBarStateController.addCallback(this);
@@ -434,11 +440,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mShowing) {
// If we were showing the bouncer and then aborting, we need to also clear out any
// potential actions unless we actually unlocked.
- mAfterKeyguardGoneAction = null;
- if (mKeyguardGoneCancelAction != null) {
- mKeyguardGoneCancelAction.run();
- mKeyguardGoneCancelAction = null;
- }
+ cancelPostAuthActions();
}
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
@@ -474,6 +476,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return;
}
+ if (mAlternateAuthInterceptor != null
+ && mAlternateAuthInterceptor.showAlternativeAuthMethod()) {
+ mStatusBar.updateScrimController();
+ mAfterKeyguardGoneAction = r;
+ mKeyguardGoneCancelAction = cancelAction;
+ return;
+ }
+
if (!afterKeyguardGone) {
mBouncer.showWithDismissAction(r, cancelAction);
} else {
@@ -508,11 +518,21 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing);
}
+ resetAlternateAuth();
mKeyguardUpdateManager.sendKeyguardReset();
updateStates();
}
}
+ /**
+ * Stop showing any alternate auth methods
+ */
+ public void resetAlternateAuth() {
+ if (mAlternateAuthInterceptor != null && mAlternateAuthInterceptor.reset()) {
+ mStatusBar.updateScrimController();
+ }
+ }
+
@Override
public void onStartedWakingUp() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
@@ -834,6 +854,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return mBouncer.isFullscreenBouncer();
}
+ /**
+ * Clear out any potential actions that were saved to run when the device is unlocked
+ */
+ public void cancelPostAuthActions() {
+ if (bouncerIsOrWillBeShowing()) {
+ return; // allow bouncer to trigger saved actions
+ }
+ mAfterKeyguardGoneAction = null;
+ if (mKeyguardGoneCancelAction != null) {
+ mKeyguardGoneCancelAction.run();
+ mKeyguardGoneCancelAction = null;
+ }
+ }
+
private long getNavBarShowDelay() {
if (mKeyguardStateController.isKeyguardFadingAway()) {
return mKeyguardStateController.getKeyguardFadingAwayDelay();
@@ -1063,6 +1097,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer != null) {
mBouncer.dump(pw);
}
+
+ if (mAlternateAuthInterceptor != null) {
+ pw.println("AltAuthInterceptor: ");
+ mAlternateAuthInterceptor.dump(pw);
+ }
}
@Override
@@ -1079,6 +1118,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return mBouncer;
}
+ public boolean isShowingAlternativeAuth() {
+ return mAlternateAuthInterceptor != null
+ && mAlternateAuthInterceptor.isShowingAlternativeAuth();
+ }
+
+ public boolean isShowingAlternativeAuthOrAnimating() {
+ return mAlternateAuthInterceptor != null
+ && (mAlternateAuthInterceptor.isShowingAlternativeAuth()
+ || mAlternateAuthInterceptor.isAnimating());
+ }
+
private static class DismissWithActionRequest {
final OnDismissAction dismissAction;
final Runnable cancelAction;
@@ -1093,4 +1143,36 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
this.message = message;
}
}
+
+ /**
+ * Delegate used to send show/reset events to an alternate authentication method instead of the
+ * bouncer.
+ */
+ public interface AlternateAuthInterceptor {
+ /**
+ * @return whether alternative auth method was newly shown
+ */
+ boolean showAlternativeAuthMethod();
+
+ /**
+ * reset the state to the default (only keyguard showing, no auth methods showing)
+ * @return whether alternative auth method was newly hidden
+ */
+ boolean reset();
+
+ /**
+ * @return true if alternative auth method is showing
+ */
+ boolean isShowingAlternativeAuth();
+
+ /**
+ * print information for the alternate auth interceptor registered
+ */
+ void dump(PrintWriter pw);
+
+ /**
+ * @return true if the new auth method is currently animating in or out.
+ */
+ boolean isAnimating();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 34673f2503ce..801ac964777b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -356,9 +356,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (isActivityIntent || canBubble) {
mAssistManagerLazy.get().hideAssist();
}
- if (shouldCollapse()) {
- collapseOnMainThread();
- }
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(entry);
@@ -408,6 +405,12 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mMainThreadHandler.post(
() -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
}
+
+ // expandStackAndSelectBubble won't affect shouldCollapse, so we can collapse directly even
+ // if we are not on the main thread.
+ if (shouldCollapse()) {
+ collapseOnMainThread();
+ }
}
private void startNotificationIntent(
@@ -438,6 +441,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
null, null, options);
mMainThreadHandler.post(() -> {
mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
+ if (shouldCollapse()) {
+ collapseOnMainThread();
+ }
});
} catch (RemoteException | PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
@@ -465,11 +471,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mActivityLaunchAnimator.setLaunchResult(launchResult,
true /* isActivityIntent */);
removeHUN(row);
+ if (shouldCollapse()) {
+ mCommandQueue.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */);
+ }
});
- if (shouldCollapse()) {
- mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
- }
});
return true;
}, null, false /* afterKeyguardGone */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 59c1138431fb..461587760f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -26,6 +26,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardConstants;
@@ -143,6 +144,16 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
openQsUserPanel();
});
+ mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mContext.getString(
+ R.string.accessibility_quick_settings_choose_user_action)));
+ }
+ });
+
updateView(true /* forceUpdate */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index fbdaf9cdae20..db039b44d91a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
@@ -307,7 +308,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback);
}
- ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
+ NetworkCallback callback =
+ new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO){
private Network mLastNetwork;
private NetworkCapabilities mLastNetworkCapabilities;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 320b00af0fc5..f72d2ae191d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -31,8 +31,15 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.ServiceManager;
@@ -70,6 +77,7 @@ import androidx.annotation.NonNull;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -137,12 +145,40 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
+ /**
+ * The remote view needs to adapt to colorized notifications when set
+ * @param color colorized notification color
+ */
+ public void overrideBackgroundTintColor(int color) {
+ mEditText.setBackgroundTintColor(color);
+ final boolean dark = !ContrastColorUtil.isColorLight(color);
+ int[][] states = new int[][] {
+ new int[] {android.R.attr.state_enabled},
+ new int[] {},
+ };
+
+ final int finalColor = dark
+ ? Color.WHITE
+ : Color.BLACK;
+
+ int[] colors = new int[] {
+ finalColor,
+ finalColor & 0x4DFFFFFF // %30 opacity
+ };
+
+ final ColorStateList tint = new ColorStateList(states, colors);
+ mSendButton.setImageTintList(tint);
+ mProgressBar.setProgressTintList(tint);
+ mProgressBar.setIndeterminateTintList(tint);
+ mProgressBar.setSecondaryProgressTintList(tint);
+ mEditText.setForegroundColor(finalColor);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mProgressBar = findViewById(R.id.remote_input_progress);
-
mSendButton = findViewById(R.id.remote_input_send);
mSendButton.setOnClickListener(this);
@@ -362,6 +398,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
}
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mEditText.updateCornerRadius(heightMeasureSpec / 2);
+ }
+
/** Populates the text field of the remote input with the given content. */
public void setEditTextContent(@Nullable CharSequence editTextContent) {
mEditText.setText(editTextContent);
@@ -651,17 +693,47 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent;
- private final Drawable mBackground;
private RemoteInputView mRemoteInputView;
+ private GradientDrawable mTextBackground;
+ private ColorDrawable mBackgroundColor;
+ private LayerDrawable mBackground;
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
private InputMethodManager mInputMethodManager;
+ private int mColor = Notification.COLOR_DEFAULT;
UserHandle mUser;
+ private int mStokeWidth;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
- mBackground = getBackground();
mLightBarController = Dependency.get(LightBarController.class);
+ mTextBackground = createBackground(context, attrs);
+ mBackgroundColor = new ColorDrawable();
+ mBackground = new LayerDrawable(new Drawable[] {mBackgroundColor, mTextBackground});
+ float density = context.getResources().getDisplayMetrics().density;
+ mStokeWidth = (int) (2 * density);
+ setDefaultColors();
+ }
+
+ private void setDefaultColors() {
+ Resources.Theme theme = getContext().getTheme();
+ TypedArray ta = theme.obtainStyledAttributes(
+ new int[]{android.R.attr.colorAccent,
+ com.android.internal.R.attr.colorBackgroundFloating});
+ mTextBackground.setStroke(mStokeWidth,
+ ta.getColor(0, Notification.COLOR_DEFAULT));
+ mColor = ta.getColor(1, Notification.COLOR_DEFAULT);
+ mTextBackground.setColor(mColor);
+ }
+
+ private GradientDrawable createBackground(Context context, AttributeSet attrs) {
+ float density = context.getResources().getDisplayMetrics().density;
+ int padding = (int) (12 * density);
+ GradientDrawable d = new GradientDrawable();
+ d.setShape(GradientDrawable.RECTANGLE);
+ d.setPadding(padding, padding, padding, padding);
+ d.setCornerRadius(padding);
+ return d;
}
void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) {
@@ -724,6 +796,19 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
}
+ protected void setBackgroundTintColor(int color) {
+ mBackgroundColor.setColor(color);
+ mTextBackground.setColor(color);
+ }
+
+ protected void setForegroundColor(int color) {
+ mTextBackground.setStroke(mStokeWidth, color);
+ setTextColor(color);
+ // %60
+ setHintTextColor(color & 0x99FFFFFF);
+ setTextCursorDrawable(null);
+ }
+
@Override
public void getFocusedRect(Rect r) {
super.getFocusedRect(r);
@@ -811,6 +896,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
setSelection(getText().length());
}
+ void updateCornerRadius(float radius) {
+ mTextBackground.setCornerRadius(radius);
+ }
+
void setInnerFocusable(boolean focusable) {
setFocusableInTouchMode(focusable);
setFocusable(focusable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index e8331a176134..e76b8035cd59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
import android.app.admin.DeviceAdminInfo;
+import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import com.android.systemui.Dumpable;
@@ -33,6 +34,10 @@ public interface SecurityController extends CallbackController<SecurityControlle
String getProfileOwnerName();
CharSequence getDeviceOwnerOrganizationName();
CharSequence getWorkProfileOrganizationName();
+ /** Device owner component even if not on this user. **/
+ ComponentName getDeviceOwnerComponentOnAnyUser();
+ /** Device owner type for a device owner. **/
+ int getDeviceOwnerType(ComponentName admin);
boolean isNetworkLoggingEnabled();
boolean isVpnEnabled();
boolean isVpnRestricted();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 5638503be198..4afb86b1a810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -15,9 +15,11 @@
*/
package com.android.systemui.statusbar.policy;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.DeviceOwnerType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -225,6 +227,18 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
}
@Override
+ @Nullable
+ public ComponentName getDeviceOwnerComponentOnAnyUser() {
+ return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();
+ }
+
+ @Override
+ @DeviceOwnerType
+ public int getDeviceOwnerType(@NonNull ComponentName admin) {
+ return mDevicePolicyManager.getDeviceOwnerType(admin);
+ }
+
+ @Override
public boolean isNetworkLoggingEnabled() {
return mDevicePolicyManager.isNetworkLoggingEnabled(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index fab1655b1262..fd19528e6d55 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -19,14 +19,13 @@ package com.android.systemui.toast;
import android.animation.Animator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -35,6 +34,8 @@ import android.widget.ToastPresenter;
import com.android.internal.R;
import com.android.launcher3.icons.IconFactory;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.systemui.plugins.ToastPlugin;
/**
@@ -170,8 +171,13 @@ public class SystemUIToast implements ToastPlugin.Toast {
com.android.systemui.R.layout.text_toast, null);
((TextView) toastView.findViewById(com.android.systemui.R.id.text)).setText(mText);
- ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon))
- .setImageDrawable(getBadgedIcon(mContext, mPackageName, mUserId));
+ Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId);
+ if (icon == null) {
+ toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE);
+ } else {
+ ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon))
+ .setImageDrawable(icon);
+ }
} else {
toastView = ToastPresenter.getTextToastView(mContext, mText);
}
@@ -217,18 +223,20 @@ public class SystemUIToast implements ToastPlugin.Toast {
*/
public static Drawable getBadgedIcon(@NonNull Context context, String packageName,
int userId) {
- final PackageManager packageManager = context.getPackageManager();
- try {
- final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
- packageName, PackageManager.GET_META_DATA, userId);
- UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
- IconFactory iconFactory = IconFactory.obtain(context);
- Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
- appInfo.loadUnbadgedIcon(packageManager), user, false).icon;
- return new BitmapDrawable(context.getResources(), iconBmp);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e("SystemUIToast", "could not load icon for package=" + packageName + " e=" + e);
+ final ApplicationsState appState =
+ ApplicationsState.getInstance((Application) context.getApplicationContext());
+ final AppEntry appEntry = appState.getEntry(packageName, userId);
+
+ if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) {
return null;
}
+
+ final ApplicationInfo appInfo = appEntry.info;
+ UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
+ IconFactory iconFactory = IconFactory.obtain(context);
+ Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
+ appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon;
+ return new BitmapDrawable(context.getResources(), iconBmp);
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index ba58ed282786..5cd3e5787a49 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -52,6 +52,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -408,14 +409,15 @@ public class GarbageMonitor implements Dumpable {
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
GarbageMonitor monitor
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
gm = monitor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index a12326961b08..3f4ec8539d38 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -480,8 +480,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static StartingWindowController provideStartingWindowController(Context context,
- @ShellSplashscreenThread ShellExecutor executor) {
- return new StartingWindowController(context, executor);
+ @ShellSplashscreenThread ShellExecutor executor, TransactionPool pool) {
+ return new StartingWindowController(context, executor, pool);
}
//
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index c2ade81a9877..0cf343c2d3c4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -36,6 +36,8 @@ import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.classifier.FalsingCollectorFake;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +71,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
private LatencyTracker mLatencyTracker;
+ private final FalsingCollector mFalsingCollector = new FalsingCollectorFake();
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
@@ -84,7 +87,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
.thenReturn(mKeyguardMessageArea);
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector) {
@Override
void resetState() {
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index c69ec1a254c3..fc93dedc4e8e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,6 +24,8 @@ import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,6 +50,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
@Mock
private lateinit var mLatencyTracker: LatencyTracker
+ private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
@Mock
private lateinit
var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@@ -72,7 +75,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
.thenReturn(mKeyguardMessageAreaController)
mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mLatencyTracker, mKeyguardMessageAreaControllerFactory)
+ mLatencyTracker, mFalsingCollector, mKeyguardMessageAreaControllerFactory)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 31cc7bb7c958..33a0dcd048ae 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -33,6 +33,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.SingleTapClassifier;
import org.junit.Before;
import org.junit.Test;
@@ -69,9 +70,12 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
private LiftToActivateListener mLiftToactivateListener;
private FalsingCollector mFalsingCollector = new FalsingCollectorFake();
@Mock
+ private SingleTapClassifier mSingleTapClassifier;
+ @Mock
private View mDeleteButton;
@Mock
private View mOkButton;
+ private NumPadKey[] mButtons = new NumPadKey[]{};
private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@@ -83,6 +87,7 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
+ when(mPinBasedInputView.getButtons()).thenReturn(mButtons);
when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
.thenReturn(mKeyguardMessageArea);
when(mPinBasedInputView.findViewById(R.id.delete_button))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index b03dc94fde33..096ce0f9de27 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -16,13 +16,19 @@
package com.android.keyguard;
+import static android.view.WindowInsets.Type.ime;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowInsetsController;
@@ -33,6 +39,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -82,21 +89,38 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
@Mock
private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
@Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
private ConfigurationController mConfigurationController;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+ private KeyguardPasswordViewController mKeyguardPasswordViewController;
+ private KeyguardPasswordView mKeyguardPasswordView;
@Before
public void setup() {
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+ mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
+ when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
+ when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+ mKeyguardPasswordViewController = new KeyguardPasswordViewController(
+ (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
+ SecurityMode.Password, mLockPatternUtils, null,
+ mKeyguardMessageAreaControllerFactory, null, null, null, mock(Resources.class),
+ null);
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController).create(mSecurityCallback);
+ mConfigurationController)
+ .create(mSecurityCallback);
}
@Test
@@ -119,14 +143,13 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
public void startDisappearAnimation_animatesKeyboard() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
SecurityMode.Password);
- when(mInputViewController.getSecurityMode()).thenReturn(
- SecurityMode.Password);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
- .thenReturn(mInputViewController);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
mKeyguardSecurityContainerController.startDisappearAnimation(null);
- verify(mInputViewController).startDisappearAnimation(eq(null));
+ verify(mWindowInsetsController).controlWindowInsetsAnimation(
+ eq(ime()), anyLong(), any(), any(), any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 1783fa4112b8..104318e0f4ae 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,9 +19,6 @@ package com.android.keyguard;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -90,13 +87,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
- public void startDisappearAnimation_animatesKeyboard() {
- mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
- verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
- any(), any());
- }
-
- @Test
public void onMeasure_usesFullWidthWithoutOneHandedMode() {
setUpKeyguard(
/* deviceConfigCanUseOneHandedKeyguard= */false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index eb95d1653e84..e35e9877998c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -195,7 +195,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal(
0 /* id */,
FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */,
- false /* supportsFaceDetection */, true /* supportsSelfIllumination */));
+ false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */));
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -793,36 +794,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void testStartUdfpsServiceOnShadeLocked() {
- // GIVEN
- // - bouncer isn't showing
- // - user has authenticated since boot
- setKeyguardBouncerVisibility(false /* isVisible */);
- when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
-
- // WHEN the status bar state changes to SHADE_LOCKED
- mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
-
- // THEN we shouldn't listen for udfps
- assertThat(mKeyguardUpdateMonitor.shouldListenForUdfps()).isEqualTo(false);
- }
-
- @Test
- public void testStartUdfpsServiceOnFullscreenUserSwitcher() {
- // GIVEN
- // - bouncer isn't showing
- // - user has authenticated since boot
- setKeyguardBouncerVisibility(false /* isVisible */);
- when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
-
- // WHEN the status bar state changes to FULLSCREEN_USER_SWITCHER
- mStatusBarStateListener.onStateChanged(StatusBarState.FULLSCREEN_USER_SWITCHER);
-
- // THEN we shouldn't listen for udfps
- assertThat(mKeyguardUpdateMonitor.shouldListenForUdfps()).isEqualTo(false);
- }
-
- @Test
public void testStartUdfpsServiceNoAuthenticationSinceLastBoot() {
// GIVEN
// - bouncer isn't showing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index 49282ee360e2..7f8be9125316 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -22,10 +22,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.biometrics.PromptInfo;
@@ -333,16 +331,50 @@ public class AuthBiometricViewTest extends SysuiTestCase {
}
@Test
- public void testUdfpsBottomSpacerCalculation() {
+ public void testUdfpsBottomSpacerHeightForPortrait() {
final int displayHeightPx = 3000;
final int navbarHeightPx = 10;
final int dialogBottomMarginPx = 20;
+ final int buttonBarHeightPx = 100;
+ final int textIndicatorHeightPx = 200;
- final View buttonBar = mock(View.class);
- when(buttonBar.getMeasuredHeight()).thenReturn(100);
+ final int sensorLocationX = 540;
+ final int sensorLocationY = 1600;
+ final int sensorRadius = 100;
+ final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
+ 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
+ sensorRadius);
+
+ assertEquals(970,
+ AuthBiometricUdfpsView.calculateBottomSpacerHeightForPortrait(
+ props, displayHeightPx, textIndicatorHeightPx, buttonBarHeightPx,
+ dialogBottomMarginPx, navbarHeightPx
+ ));
+ }
- final View textIndicator = mock(View.class);
- when(textIndicator.getMeasuredHeight()).thenReturn(200);
+ @Test
+ public void testUdfpsBottomSpacerHeightForLandscape() {
+ final int titleHeightPx = 320;
+ final int subtitleHeightPx = 240;
+ final int descriptionHeightPx = 200;
+ final int topSpacerHeightPx = 550;
+ final int textIndicatorHeightPx = 190;
+ final int buttonBarHeightPx = 160;
+ final int navbarBottomInsetPx = 75;
+
+ assertEquals(885,
+ AuthBiometricUdfpsView.calculateBottomSpacerHeightForLandscape(
+ titleHeightPx, subtitleHeightPx, descriptionHeightPx, topSpacerHeightPx,
+ textIndicatorHeightPx, buttonBarHeightPx, navbarBottomInsetPx));
+ }
+
+ @Test
+ public void testUdfpsHorizontalSpacerWidthForLandscape() {
+ final int displayWidthPx = 3000;
+ final int dialogMarginPx = 20;
+ final int navbarHorizontalInsetPx = 75;
final int sensorLocationX = 540;
final int sensorLocationY = 1600;
@@ -353,9 +385,9 @@ public class AuthBiometricViewTest extends SysuiTestCase {
true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
sensorRadius);
- assertEquals(970, AuthBiometricUdfpsView.calculateBottomSpacerHeight(
- displayHeightPx, navbarHeightPx, dialogBottomMarginPx, buttonBar, textIndicator,
- props));
+ assertEquals(1205,
+ AuthBiometricUdfpsView.calculateHorizontalSpacerWidthForLandscape(
+ props, displayWidthPx, dialogMarginPx, navbarHorizontalInsetPx));
}
private PromptInfo buildPromptInfo(boolean allowDeviceCredential) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 07686181649d..be110fcf70ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -44,8 +44,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -91,6 +93,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private StatusBar mStatusBar;
@Mock
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock
+ private DumpManager mDumpManager;
+ @Mock
private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
private FakeExecutor mFgExecutor;
@@ -129,7 +135,9 @@ public class UdfpsControllerTest extends SysuiTestCase {
mWindowManager,
mStatusBarStateController,
mFgExecutor,
- mStatusBar);
+ mStatusBar,
+ mStatusBarKeyguardViewManager,
+ mDumpManager);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
@@ -246,20 +254,4 @@ public class UdfpsControllerTest extends SysuiTestCase {
// THEN the illumination is hidden
verify(mUdfpsView).stopIllumination();
}
-
- @Test
- public void registersAndUnregistersViewForCallbacks() throws RemoteException {
- mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
- verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
- verify(mStatusBar).addExpansionChangedListener(
- mUdfpsController.mStatusBarExpansionListener);
-
- mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
- mFgExecutor.runAllReady();
- verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener);
- verify(mStatusBar).removeExpansionChangedListener(
- mUdfpsController.mStatusBarExpansionListener);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
new file mode 100644
index 000000000000..65f0f7b87cf4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
+ // Dependencies
+ @Mock
+ private UdfpsKeyguardView mView;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private StatusBar mStatusBar;
+ @Mock
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock
+ private DumpManager mDumpManager;
+
+ private UdfpsKeyguardViewController mController;
+
+ // Capture listeners so that they can be used to send events
+ @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+ private StatusBarStateController.StateListener mParentStatusBarStateListener;
+ private StatusBarStateController.StateListener mStatusBarStateListener;
+
+ @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
+ private StatusBar.ExpansionChangedListener mExpansionListener;
+
+ @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
+ mAltAuthInterceptorCaptor;
+ private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new UdfpsKeyguardViewController(
+ mView,
+ mStatusBarStateController,
+ mStatusBar,
+ mStatusBarKeyguardViewManager,
+ mDumpManager);
+ }
+
+ @Test
+ public void testRegistersExpansionChangedListenerOnAttached() {
+ mController.onViewAttached();
+ captureExpansionListener();
+ }
+
+ @Test
+ public void testRegistersStatusBarStateListenersOnAttached() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ }
+
+ @Test
+ public void testViewControllerQueriesSBStateOnAttached() {
+ mController.onViewAttached();
+ verify(mStatusBarStateController, times(2)).getState();
+ verify(mStatusBarStateController).getDozeAmount();
+
+ final float dozeAmount = .88f;
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount);
+ captureStatusBarStateListeners();
+
+ mController.onViewAttached();
+ verify(mView).setPauseAuth(true);
+ verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount);
+ }
+
+ @Test
+ public void testListenersUnregisteredOnDetached() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureExpansionListener();
+ mController.onViewDetached();
+
+ verify(mStatusBarStateController).removeCallback(mParentStatusBarStateListener);
+ verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
+ verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
+ }
+
+ @Test
+ public void testDozeEventsSentToView() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+
+ final float linear = .55f;
+ final float eased = .65f;
+ mStatusBarStateListener.onDozeAmountChanged(linear, eased);
+
+ verify(mView).onDozeAmountChanged(linear, eased);
+ }
+
+ @Test
+ public void testShouldNotPauseAuthOnKeyguard() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureExpansionListener();
+
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+ assertFalse(mController.shouldPauseAuth());
+ }
+
+ @Test
+ public void testShouldPauseAuthOnShadeLocked() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureExpansionListener();
+
+ sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
+
+ assertTrue(mController.shouldPauseAuth());
+ }
+
+ @Test
+ public void testOverrideShouldPauseAuthOnShadeLocked() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureAltAuthInterceptor();
+
+ sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
+ assertTrue(mController.shouldPauseAuth());
+
+ mAltAuthInterceptor.showAlternativeAuthMethod(); // force show
+ assertFalse(mController.shouldPauseAuth());
+ assertTrue(mAltAuthInterceptor.isShowingAlternativeAuth());
+
+ mAltAuthInterceptor.reset(); // stop force show
+ assertTrue(mController.shouldPauseAuth());
+ assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth());
+ }
+
+ @Test
+ public void testOnDetachedStateReset() {
+ // GIVEN view is attached, alt auth is force being shown
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureAltAuthInterceptor();
+
+ mAltAuthInterceptor.showAlternativeAuthMethod(); // alt auth force show
+
+ // WHEN view is detached
+ mController.onViewDetached();
+
+ // THEN alt auth state reports not showing
+ assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth());
+ }
+
+ private void sendStatusBarStateChanged(int statusBarState) {
+ mStatusBarStateListener.onStateChanged(statusBarState);
+ mParentStatusBarStateListener.onStateChanged(statusBarState);
+ }
+
+ private void captureStatusBarStateListeners() {
+ verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture());
+ List<StatusBarStateController.StateListener> stateListeners =
+ mStateListenerCaptor.getAllValues();
+ mParentStatusBarStateListener = stateListeners.get(0);
+ mStatusBarStateListener = stateListeners.get(1);
+ }
+
+ private void captureExpansionListener() {
+ verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture());
+ mExpansionListener = mExpansionListenerCaptor.getValue();
+ }
+
+ private void captureAltAuthInterceptor() {
+ verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor(
+ mAltAuthInterceptorCaptor.capture());
+ mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 5709ce3035a2..d015f51055b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -21,10 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,6 +35,7 @@ import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingDataProvider.GestureCompleteListener;
import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -74,7 +72,10 @@ public class BrightLineClassifierTest extends SysuiTestCase {
private FalsingClassifier mClassifierB;
private final List<MotionEvent> mMotionEventList = new ArrayList<>();
@Mock
- private HistoryTracker mHistoryTracker;;
+ private HistoryTracker mHistoryTracker;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+
private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, "");
@@ -91,9 +92,10 @@ public class BrightLineClassifierTest extends SysuiTestCase {
mClassifiers.add(mClassifierA);
mClassifiers.add(mClassifierB);
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager,
mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier,
- mHistoryTracker, mFakeExecutor, DOUBLE_TAP_TIMEOUT_MS, false);
+ mHistoryTracker, mKeyguardStateController, false);
ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor =
@@ -123,7 +125,7 @@ public class BrightLineClassifierTest extends SysuiTestCase {
}
@Test
- public void testIsFalseTouch_ClassffiersPass() {
+ public void testIsFalseTouch_ClassifiersPass() {
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
}
@@ -161,25 +163,27 @@ public class BrightLineClassifierTest extends SysuiTestCase {
public void testIsFalseTap_BasicCheck() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
- assertThat(mBrightLineFalsingManager.isFalseTap(false)).isTrue();
+ assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isTrue();
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
- assertThat(mBrightLineFalsingManager.isFalseTap(false)).isFalse();
+ assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
}
@Test
public void testIsFalseTap_RobustCheck_NoFaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+ when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult);
+ when(mHistoryTracker.falseBelief()).thenReturn(1.0);
mFalsingDataProvider.setJustUnlockedWithFace(false);
- assertThat(mBrightLineFalsingManager.isFalseTap(true)).isTrue();
+ assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue();
}
@Test
public void testIsFalseTap_RobustCheck_FaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
- assertThat(mBrightLineFalsingManager.isFalseTap(true)).isFalse();
+ assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isFalse();
}
@Test
@@ -203,43 +207,29 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test
public void testHistory_singleTap() {
// When trying to classify single taps, we don't immediately add results to history.
- mBrightLineFalsingManager.isFalseTap(false);
+ mBrightLineFalsingManager.isFalseTap(false, 0);
mGestureCompleteListener.onGestureComplete(1000);
-
- verify(mHistoryTracker, never()).addResults(any(), anyLong());
-
- mFakeExecutor.advanceClockToNext();
- mFakeExecutor.runAllReady();
-
verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
}
@Test
public void testHistory_multipleSingleTaps() {
// When trying to classify single taps, we don't immediately add results to history.
- mBrightLineFalsingManager.isFalseTap(false);
+ mBrightLineFalsingManager.isFalseTap(false, 0);
mGestureCompleteListener.onGestureComplete(1000);
- mBrightLineFalsingManager.isFalseTap(false);
+ mBrightLineFalsingManager.isFalseTap(false, 0);
mGestureCompleteListener.onGestureComplete(2000);
-
- verify(mHistoryTracker, never()).addResults(any(), anyLong());
-
- mFakeExecutor.advanceClockToNext();
- mFakeExecutor.runNextReady();
verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
- reset(mHistoryTracker);
- mFakeExecutor.advanceClockToNext();
- mFakeExecutor.runNextReady();
verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
}
@Test
public void testHistory_doubleTap() {
// When trying to classify single taps, we don't immediately add results to history.
- mBrightLineFalsingManager.isFalseTap(false);
+ mBrightLineFalsingManager.isFalseTap(false, 0);
mGestureCompleteListener.onGestureComplete(1000);
// Before checking for double tap, we may check for single-tap on the second gesture.
- mBrightLineFalsingManager.isFalseTap(false);
+ mBrightLineFalsingManager.isFalseTap(false, 0);
mBrightLineFalsingManager.isFalseDoubleTap();
mGestureCompleteListener.onGestureComplete(2000);
@@ -248,4 +238,18 @@ public class BrightLineClassifierTest extends SysuiTestCase {
assertThat(mFakeExecutor.numPending()).isEqualTo(0);
}
+
+ @Test
+ public void testNoFalsingUnlocked() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
+
+ when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
+
+ when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
index 472ed7a22fe3..ca0a4aa2b04d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
@@ -21,7 +21,6 @@ import static com.android.systemui.classifier.Classifier.UNLOCK;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.FakeBatteryController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -45,8 +44,7 @@ public class ClassifierTest extends LeakCheckedTest {
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
mFakeBatteryController = new FakeBatteryController(getLeakCheck());
- mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController,
- new FakeSystemClock());
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
mDataProvider.setInteractionType(UNLOCK);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
index 17c2700c5bda..4e1742497d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.classifier;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -35,8 +34,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Deque;
-import java.util.LinkedList;
import java.util.List;
@SmallTest
@@ -47,7 +44,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
private static final long DOUBLE_TAP_TIMEOUT_MS = 100;
private List<MotionEvent> mMotionEvents = new ArrayList<>();
- private final Deque<List<MotionEvent>> mHistoricalMotionEvents = new LinkedList<>();
+ private List<MotionEvent> mPriorMotionEvents = new ArrayList<>();
@Mock
private FalsingDataProvider mDataProvider;
@@ -64,7 +61,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
MockitoAnnotations.initMocks(this);
mClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, TOUCH_SLOP,
DOUBLE_TAP_TIMEOUT_MS);
- doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents();
+ when(mDataProvider.getPriorMotionEvents()).thenReturn(mPriorMotionEvents);
}
@After
@@ -177,9 +174,8 @@ public class DoubleTapClassifierTest extends ClassifierTest {
}
private void archiveMotionEvents() {
- mHistoricalMotionEvents.addFirst(mMotionEvents);
- doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents();
+ mPriorMotionEvents = mMotionEvents;
+ when(mDataProvider.getPriorMotionEvents()).thenReturn(mPriorMotionEvents);
mMotionEvents = new ArrayList<>();
-
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 23ef865750b2..e6aeee7a9184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -34,8 +34,10 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -56,18 +58,24 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
+ private HistoryTracker mHistoryTracker;
+ @Mock
private ProximitySensor mProximitySensor;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
- mKeyguardUpdateMonitor, mProximitySensor, mStatusBarStateController);
+ mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
+ mStatusBarStateController, mKeyguardStateController, new FakeSystemClock());
}
@Test
@@ -155,4 +163,20 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
mFalsingCollector.onTouchEvent(up);
verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
}
+
+ @Test
+ public void testAvoidUnlocked() {
+ MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ // Nothing passed initially
+ mFalsingCollector.onTouchEvent(down);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+ // Up event would normally flush the up event.
+ mFalsingCollector.onTouchEvent(up);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index be0cc97a2f0f..ebdda67a90d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -26,7 +26,6 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.FakeBatteryController;
import org.junit.After;
@@ -52,8 +51,7 @@ public class FalsingDataProviderTest extends ClassifierTest {
displayMetrics.ydpi = 100;
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
- mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController,
- new FakeSystemClock());
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
index 01cce3579b0c..bb7545f93b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
@@ -48,14 +48,14 @@ public class HistoryTrackerTest extends SysuiTestCase {
@Test
public void testNoDataNoPenalty() {
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0);
+ assertThat(mHistoryTracker.falseBelief()).isEqualTo(0.5);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0);
}
@Test
public void testOneResultFullConfidence() {
addResult(true, 1);
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
}
@@ -64,8 +64,8 @@ public class HistoryTrackerTest extends SysuiTestCase {
addResult(true, 1);
addResult(false, 1);
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0.5);
- assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0.5);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(0.5);
+ assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(0.5);
}
@Test
@@ -73,20 +73,20 @@ public class HistoryTrackerTest extends SysuiTestCase {
addResult(true, 1);
addResult(true, 0);
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0.75);
- assertThat(mHistoryTracker.falseConfidence()).isEqualTo(.75);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
+ assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(.75);
}
@Test
public void testDecay() {
addResult(true, 1);
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
- mSystemClock.advanceTime(1000);
+ mSystemClock.advanceTime(9999);
- assertThat(mHistoryTracker.falsePenalty()).isWithin(0.01).of(0.1);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.005).of(0.55);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
}
@@ -96,25 +96,25 @@ public class HistoryTrackerTest extends SysuiTestCase {
mSystemClock.advanceTime(1000);
addResult(false, .5);
- assertThat(mHistoryTracker.falsePenalty()).isWithin(0.01).of(0.17);
- assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0.625);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.01).of(0.74);
+ assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(0.625);
}
@Test
public void testCompleteDecay() {
addResult(true, 1);
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+ assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
- mSystemClock.advanceTime(2999);
+ mSystemClock.advanceTime(9999);
- assertThat(mHistoryTracker.falsePenalty()).isGreaterThan(0);
+ assertThat(mHistoryTracker.falseBelief()).isGreaterThan(0);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
mSystemClock.advanceTime(1);
- assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0);
+ assertThat(mHistoryTracker.falseBelief()).isEqualTo(0.5);
assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java
index 6e312594a2e4..901196f8bbba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java
@@ -89,20 +89,4 @@ public class TimeLimitedMotionEventBufferTest extends SysuiTestCase {
assertThat(mBuffer.get(0), is(eventC));
assertThat(mBuffer.get(1), is(eventD));
}
-
- @Test
- public void testFullyExpired() {
- MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
- MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0);
- MotionEvent eventC = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 0, 0, 0);
- MotionEvent eventD = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 0, 0, 0);
-
- mBuffer.add(eventA);
- mBuffer.add(eventB);
- mBuffer.add(eventC);
- mBuffer.add(eventD);
-
- assertThat(mBuffer.isFullyExpired(2), is(false));
- assertThat(mBuffer.isFullyExpired(6), is(true));
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 9278570714fe..17eb15b6a556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.controls.ui
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -63,6 +64,8 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
private lateinit var taskViewFactory: Optional<TaskViewFactory>
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var cvh: ControlViewHolder
+ @Mock
+ private lateinit var metricsLogger: ControlsMetricsLogger
companion object {
fun <T> any(): T = Mockito.any<T>()
@@ -86,7 +89,8 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
globalActionsComponent,
taskViewFactory,
getFakeBroadcastDispatcher(),
- lazyUiController
+ lazyUiController,
+ metricsLogger
))
`when`(cvh.cws.ci.controlId).thenReturn(ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 9fd9b470a83b..1aeb0d8a5361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -60,6 +60,7 @@ public class DozeConfigurationUtil {
when(config.tapSensorType()).thenReturn(null);
when(config.longPressSensorType()).thenReturn(null);
when(config.udfpsLongPressSensorType()).thenReturn(null);
+ when(config.quickPickupSensorType()).thenReturn(null);
when(config.tapGestureEnabled(anyInt())).thenReturn(true);
when(config.tapSensorAvailable()).thenReturn(true);
@@ -67,6 +68,7 @@ public class DozeConfigurationUtil {
when(config.dozePickupSensorAvailable()).thenReturn(false);
when(config.wakeScreenGestureAvailable()).thenReturn(false);
+ when(config.quickPickupSensorEnabled(anyInt())).thenReturn(false);
doneHolder[0] = true;
return config;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 27187a85c040..1817fdfd4cdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -104,7 +104,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
- mAuthController);
+ mAuthController, mExecutor, mExecutor);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 1062fae52e7a..eedf09936b4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -46,16 +46,12 @@ import android.service.dreams.IDreamManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManagerPolicyConstants;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.MetricsLogger;
@@ -251,22 +247,6 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
}
@Test
- public void testShouldLogScreenshotLongPress() {
- FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS, true);
- GlobalActionsDialog.ScreenshotAction screenshotAction =
- mGlobalActionsDialog.makeScreenshotActionForTesting();
- screenshotAction.onLongPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
-
- // Dismiss ScreenRecordDialog opened by the long press above.
- final UiObject2 cancelButton = getUiDevice().wait(
- Until.findObject(By.text(CANCEL_BUTTON)), UI_TIMEOUT_MILLIS);
- if (cancelButton != null) {
- cancelButton.click();
- }
- }
-
- @Test
public void testShouldShowScreenshot() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.integer.config_navBarInteractionMode,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 1f9862c07a4c..3d4425cf4bd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -35,6 +35,7 @@ import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -49,7 +50,7 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class SeekBarViewModelTest : SysuiTestCase() {
private lateinit var viewModel: SeekBarViewModel
@@ -124,6 +125,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateDurationWithPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
@@ -146,6 +148,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateDurationWithoutPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
@@ -204,6 +207,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateDurationNoMetadata() {
// GIVEN that the metadata is null
whenever(mockController.getMetadata()).thenReturn(null)
@@ -235,6 +239,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateSeekAvailable() {
// GIVEN that seek is included in actions
val state = PlaybackState.Builder().run {
@@ -249,6 +254,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateSeekNotAvailable() {
// GIVEN that seek is not included in actions
val state = PlaybackState.Builder().run {
@@ -303,6 +309,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun onSeekProgressWithSeekStarting() {
val pos = 42L
with(viewModel) {
@@ -314,6 +321,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun onProgressChangedFromUser() {
// WHEN user starts dragging the seek bar
val pos = 42
@@ -614,6 +622,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun clearSeekBar() {
// GIVEN that the duration is contained within the metadata
val metadata = MediaMetadata.Builder().run {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 2b76f1c2d76d..22c553b764b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -23,6 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static org.junit.Assert.assertEquals;
@@ -31,6 +32,7 @@ import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -44,12 +46,16 @@ import android.content.IntentFilter;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
@@ -79,6 +85,7 @@ import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -101,6 +108,7 @@ public class NavigationBarTest extends SysuiTestCase {
private OverviewProxyService mOverviewProxyService;
private CommandQueue mCommandQueue;
private SysUiState mMockSysUiState;
+ @Mock
private Handler mHandler;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@@ -124,12 +132,17 @@ public class NavigationBarTest extends SysuiTestCase {
mDependency.injectMockDependency(NavigationBarController.class);
mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
TestableLooper.get(this).runWithLooper(() -> {
- mHandler = new Handler();
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
}
+ @After
+ public void tearDown() throws Exception {
+ DeviceConfig.resetToDefaults(
+ Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
+ }
+
private void setupSysuiDependency() {
Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -163,6 +176,32 @@ public class NavigationBarTest extends SysuiTestCase {
}
@Test
+ public void testHomeLongPressWithCustomDuration() throws Exception {
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
+ .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
+ .build());
+ mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+
+ mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
+ /*downTime=*/SystemClock.uptimeMillis(),
+ /*eventTime=*/SystemClock.uptimeMillis(),
+ /*action=*/MotionEvent.ACTION_DOWN,
+ 0, 0, 0
+ ));
+ verify(mHandler, times(1)).postDelayed(any(), eq(100L));
+
+ mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
+ /*downTime=*/SystemClock.uptimeMillis(),
+ /*eventTime=*/SystemClock.uptimeMillis(),
+ /*action=*/MotionEvent.ACTION_UP,
+ 0, 0, 0
+ ));
+
+ verify(mHandler, times(1)).removeCallbacks(any());
+ }
+
+ @Test
public void testRegisteredWithDispatcher() {
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
verify(mBroadcastDispatcher).registerReceiverWithHandler(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 2a4b41cbfe32..410a809bd394 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -21,14 +21,16 @@ import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
-import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.REQUIRED_WIDTH_FOR_MEDIUM;
+import static com.android.systemui.people.PeopleSpaceUtils.getPeopleTileFromPersistentStorage;
+import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -58,6 +60,8 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -76,6 +80,7 @@ import android.widget.TextView;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -224,6 +229,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
@Mock
private NotificationEntryManager mNotificationEntryManager;
+ private Bundle mOptions;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -231,12 +238,12 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ mOptions = new Bundle();
+ mOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
- .thenReturn(options);
+ .thenReturn(mOptions);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
@@ -250,6 +257,10 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
mContext.getString(R.string.over_timestamp));
+ Configuration configuration = mock(Configuration.class);
+ Resources resources = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn(resources);
+ when(resources.getConfiguration()).thenReturn(configuration);
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
when(mNotificationEntryManager.getVisibleNotifications())
.thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
@@ -500,7 +511,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
.augmentTileFromVisibleNotifications(mContext, tile,
- Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+ Map.of(new PeopleTileKey(mNotificationEntry1), mNotificationEntry1));
assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
}
@@ -515,7 +526,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
.augmentTileFromVisibleNotifications(mContext, tile,
- Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+ Map.of(new PeopleTileKey(mNotificationEntry1), mNotificationEntry1));
assertThat(actual.getNotificationContent()).isEqualTo(null);
}
@@ -663,7 +674,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
@Test
public void testCreateRemoteViewsWithLastInteractionTime() {
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
- PERSON_TILE_WITHOUT_NOTIFICATION, 0);
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions);
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -674,13 +685,22 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// No availability.
View availability = result.findViewById(R.id.availability);
assertEquals(View.GONE, availability.getVisibility());
- // No new story.
- View personIcon = result.findViewById(R.id.person_icon_only);
- View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
+ // Shows person icon.
+ View personIcon = result.findViewById(R.id.person_icon);
assertEquals(View.VISIBLE, personIcon.getVisibility());
- assertEquals(View.GONE, personIconWithStory.getVisibility());
// No status.
- assertThat((View) result.findViewById(R.id.status)).isNull();
+ assertThat((View) result.findViewById(R.id.text_content)).isNull();
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
+ RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions);
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show name over predefined icon.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Shows person icon.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
}
@Test
@@ -692,7 +712,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
ACTIVITY_GAME).build())).build();
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
- tileWithAvailabilityAndNewStory, 0);
+ tileWithAvailabilityAndNewStory, 0, mOptions);
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -703,13 +723,23 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Has availability.
View availability = result.findViewById(R.id.availability);
assertEquals(View.VISIBLE, availability.getVisibility());
- // Has new story.
- View personIcon = result.findViewById(R.id.person_icon_only);
- View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
- assertEquals(View.GONE, personIcon.getVisibility());
- assertEquals(View.VISIBLE, personIconWithStory.getVisibility());
+ // Has person icon.
+ View personIcon = result.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
// No status.
- assertThat((View) result.findViewById(R.id.status)).isNull();
+ assertThat((View) result.findViewById(R.id.text_content)).isNull();
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
+ RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithAvailabilityAndNewStory, 0, mOptions);
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show name rather than game type.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
}
@Test
@@ -721,7 +751,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
ACTIVITY_BIRTHDAY).build())).build();
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusTemplate, 0);
+ tileWithStatusTemplate, 0, mOptions);
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -729,14 +759,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Has availability.
View availability = result.findViewById(R.id.availability);
assertEquals(View.VISIBLE, availability.getVisibility());
- // Has new story.
- View personIcon = result.findViewById(R.id.person_icon_only);
- View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
- assertEquals(View.GONE, personIcon.getVisibility());
- assertEquals(View.VISIBLE, personIconWithStory.getVisibility());
+ // Has person icon.
+ View personIcon = result.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
// Has status text from backup text.
- TextView statusContent = (TextView) result.findViewById(R.id.status);
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
+ RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithStatusTemplate, 0, mOptions);
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
}
@Test
@@ -746,7 +787,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
Arrays.asList(GAME_STATUS,
NEW_STORY_WITH_AVAILABILITY)).build();
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusTemplate, 0);
+ tileWithStatusTemplate, 0, mOptions);
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -754,14 +795,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Has availability.
View availability = result.findViewById(R.id.availability);
assertEquals(View.VISIBLE, availability.getVisibility());
- // Has new story.
- View personIcon = result.findViewById(R.id.person_icon_only);
- View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
- assertEquals(View.GONE, personIcon.getVisibility());
- assertEquals(View.VISIBLE, personIconWithStory.getVisibility());
+ // Has person icon.
+ View personIcon = result.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
// Has status.
- TextView statusContent = (TextView) result.findViewById(R.id.status);
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
assertEquals(statusContent.getText(), GAME_DESCRIPTION);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
+ RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithStatusTemplate, 0, mOptions);
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
}
@Test
@@ -772,7 +824,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.setNotificationContent(MISSED_CALL)
.build();
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithMissedCallNotification, 0);
+ tileWithMissedCallNotification, 0, mOptions);
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -780,16 +832,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Has availability.
View availability = result.findViewById(R.id.availability);
assertEquals(View.GONE, availability.getVisibility());
- // Has new story.
- View personIcon = result.findViewById(R.id.person_icon_only);
- View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
+ // Has person icon.
+ View personIcon = result.findViewById(R.id.person_icon);
assertEquals(View.VISIBLE, personIcon.getVisibility());
- assertEquals(View.GONE, personIconWithStory.getVisibility());
- // Has status.
- TextView statusContent = (TextView) result.findViewById(R.id.status);
+ // Has missed call notification content.
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
assertEquals(statusContent.getText(), MISSED_CALL);
- }
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
+ RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithMissedCallNotification, 0, mOptions);
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+ }
@Test
public void testCreateRemoteViewsWithNotificationTemplate() {
@@ -798,24 +859,52 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.setStatuses(Arrays.asList(GAME_STATUS,
NEW_STORY_WITH_AVAILABILITY)).build();
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusAndNotification, 0);
+ tileWithStatusAndNotification, 0, mOptions);
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
assertEquals(name.getText(), NAME);
TextView subtext = (TextView) result.findViewById(R.id.subtext);
- assertTrue(subtext.getText().toString().contains("weeks ago"));
+ assertEquals(View.GONE, subtext.getVisibility());
// Has availability.
View availability = result.findViewById(R.id.availability);
assertEquals(View.VISIBLE, availability.getVisibility());
- // Has new story.
- View personIcon = result.findViewById(R.id.person_icon_only);
- View personIconWithStory = result.findViewById(R.id.person_icon_with_story);
- assertEquals(View.GONE, personIcon.getVisibility());
- assertEquals(View.VISIBLE, personIconWithStory.getVisibility());
+ // Has person icon.
+ View personIcon = result.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
// Has notification content.
- TextView statusContent = (TextView) result.findViewById(R.id.content);
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
+ RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithStatusAndNotification, 0, mOptions);
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
+ }
+
+ @Test
+ public void testGetPeopleTileFromPersistentStorageExistingConversation()
+ throws Exception {
+ when(mPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID_1)).thenReturn(
+ getConversationChannelWithoutTimestamp(SHORTCUT_ID_1));
+ PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_1, 0, PACKAGE_NAME);
+ PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager);
+ assertThat(tile.getId()).isEqualTo(key.getShortcutId());
+ }
+
+ @Test
+ public void testGetPeopleTileFromPersistentStorageNoConversation() {
+ PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_2, 0, PACKAGE_NAME);
+ PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager);
+ assertThat(tile).isNull();
}
private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
@@ -843,4 +932,13 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
eq(shortcutId))).thenReturn(lastInteractionTimestamp);
return convo;
}
+
+ private ConversationChannel getConversationChannelWithoutTimestamp(String shortcutId)
+ throws Exception {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build();
+ ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+ 0L, false);
+ return convo;
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index f60fa099feaa..096e51b56263 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -23,10 +23,11 @@ import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
-import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
+import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
import static com.google.common.truth.Truth.assertThat;
@@ -104,13 +105,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
+ private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4;
private static final String SHORTCUT_ID = "101";
private static final String OTHER_SHORTCUT_ID = "102";
private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
- private static final String KEY = PeopleSpaceUtils.getKey(SHORTCUT_ID, TEST_PACKAGE_A, 0);
+ private static final PeopleTileKey KEY = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
private static final Person PERSON = new Person.Builder()
.setName("name")
.setKey("abc")
@@ -172,10 +174,11 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+ clearStorage();
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
@@ -395,7 +398,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
mBundleArgumentCaptor.capture());
Bundle bundle = mBundleArgumentCaptor.getValue();
- PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
assertThat(tile.getStatuses()).containsExactly(status);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
@@ -439,7 +442,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
mBundleArgumentCaptor.capture());
Bundle bundle = mBundleArgumentCaptor.getValue();
- PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
@@ -473,7 +476,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
throws Exception {
addSecondWidgetForPersonTile();
- PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ PeopleSpaceUtils.removeSharedPreferencesStorageForTile(
+ mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -510,7 +514,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
mBundleArgumentCaptor.capture());
Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
- PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
assertThat(tile.getNotificationContent())
.isEqualTo(mContext.getString(R.string.missed_call));
@@ -536,7 +540,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
mBundleArgumentCaptor.capture());
Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
- PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
@@ -547,6 +551,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
StatusBarNotification sbn = createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -560,11 +565,11 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
mBundleArgumentCaptor.capture());
Bundle bundle = mBundleArgumentCaptor.getValue();
- PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
assertThat(tile.getNotificationKey()).isEqualTo(null);
assertThat(tile.getNotificationContent()).isEqualTo(null);
assertThat(tile.getNotificationDataUri()).isEqualTo(null);
- verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+ verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
@@ -585,7 +590,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull();
assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- assertThat(sp.getStringSet(KEY, new HashSet<>())).containsExactly(
+ assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).containsExactly(
String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
// Check listener & shortcut caching remain for other widget.
verify(mPeopleManager, never()).unregisterConversationListener(any());
@@ -603,7 +608,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull();
assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull();
assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
- assertThat(sp.getStringSet(KEY, new HashSet<>())).isEmpty();
+ assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).isEmpty();
// Check listener is removed and shortcut is uncached.
verify(mPeopleManager, times(1)).unregisterConversationListener(any());
verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A),
@@ -611,13 +616,88 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
}
+ @Test
+ public void testUpdateWidgetsWithEmptyOptionsAddsPeopleTileToOptions() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(new Bundle());
+
+ mManager.updateWidgets(widgetIdsArray);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testOnAppWidgetOptionsChangedNoWidgetAdded() {
+ Bundle newOptions = new Bundle();
+ newOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
+ mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
+
+
+ // Check that options is not modified
+ verify(mAppWidgetManager, never()).updateAppWidgetOptions(
+ eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any());
+ // Check listener is not added and shortcut is not cached.
+ verify(mPeopleManager, never()).registerConversationListener(any(), anyInt(), any(), any(),
+ any());
+ verify(mLauncherApps, never()).cacheShortcuts(any(), any(), any(), anyInt());
+ // Check no added storage.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ assertThat(sp.getStringSet(KEY.toString(), new HashSet<>()))
+ .doesNotContain(SECOND_WIDGET_ID_WITH_SHORTCUT);
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ Context.MODE_PRIVATE);
+ assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
+ assertThat(widgetSp.getString(SHORTCUT_ID, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
+ assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+
+ }
+
+ @Test
+ public void testOnAppWidgetOptionsChangedWidgetAdded() {
+ Bundle newOptions = new Bundle();
+ newOptions.putString(PeopleSpaceUtils.SHORTCUT_ID, SHORTCUT_ID);
+ newOptions.putInt(USER_ID, 0);
+ newOptions.putString(PACKAGE_NAME, TEST_PACKAGE_A);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(newOptions);
+
+ mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
+
+ verify(mAppWidgetManager, times(1)).updateAppWidgetOptions(
+ eq(SECOND_WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ assertThat(bundle.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING))
+ .isEqualTo(EMPTY_STRING);
+ assertThat(bundle.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+ assertThat(bundle.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
+ verify(mLauncherApps, times(1)).cacheShortcuts(eq(TEST_PACKAGE_A),
+ eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
+ eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).contains(
+ String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ Context.MODE_PRIVATE);
+ assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(TEST_PACKAGE_A);
+ assertThat(widgetSp.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING))
+ .isEqualTo(SHORTCUT_ID);
+ assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(0);
+ }
+
/**
* Adds another widget for {@code PERSON_TILE} with widget ID: {@code
* SECOND_WIDGET_ID_WITH_SHORTCUT}.
*/
private void addSecondWidgetForPersonTile() {
Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
// Set the same Person associated on another People Tile widget ID.
@@ -676,6 +756,27 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.build();
}
+ private void clearStorage() {
+ SharedPreferences widgetSp1 = mContext.getSharedPreferences(
+ String.valueOf(WIDGET_ID_WITH_SHORTCUT),
+ Context.MODE_PRIVATE);
+ widgetSp1.edit().clear().commit();
+ SharedPreferences widgetSp2 = mContext.getSharedPreferences(
+ String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT),
+ Context.MODE_PRIVATE);
+ widgetSp2.edit().clear().commit();
+ SharedPreferences widgetSp3 = mContext.getSharedPreferences(
+ String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ Context.MODE_PRIVATE);
+ widgetSp3.edit().clear().commit();
+ SharedPreferences widgetSp4 = mContext.getSharedPreferences(
+ String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
+ Context.MODE_PRIVATE);
+ widgetSp4.edit().clear().commit();
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ sp.edit().clear().commit();
+ }
+
private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
SharedPreferences widgetSp = mContext.getSharedPreferences(
String.valueOf(widgetId),
@@ -689,7 +790,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
editor.putString(String.valueOf(widgetId), shortcutId);
- String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+ String key = new PeopleTileKey(shortcutId, 0, packageName).toString();
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
storedWidgetIds.add(String.valueOf(widgetId));
editor.putStringSet(key, storedWidgetIds);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 072f7b8a7756..791dd121852f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -395,9 +395,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
`when`(permissionManager.indicatorAppOpUsageData).thenReturn(
listOf(usage_camera, usage_location, usage_microphone)
)
- `when`(privacyItemController.micCameraAvailable).thenReturn(false)
- `when`(privacyItemController.locationAvailable).thenReturn(false)
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(true)
+ `when`(privacyItemController.micCameraAvailable).thenReturn(true)
+ `when`(privacyItemController.locationAvailable).thenReturn(true)
controller.showDialog(context)
exhaustExecutors()
@@ -422,7 +421,6 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
)
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
`when`(privacyItemController.locationAvailable).thenReturn(false)
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false)
controller.showDialog(context)
exhaustExecutors()
@@ -525,7 +523,6 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
`when`(privacyItemController.locationAvailable).thenReturn(true)
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false)
`when`(userTracker.userProfiles).thenReturn(listOf(
UserInfo(USER_ID, "", 0),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 132bee0e7fdf..f991e718122e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -37,7 +37,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -51,8 +50,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
fun <T> any(): T = Mockito.any<T>()
- private const val ALL_INDICATORS =
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
}
@@ -96,11 +93,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testNotListeningAllByDefault() {
- assertFalse(privacyItemController.allIndicatorsAvailable)
- }
-
- @Test
fun testMicCameraListeningByDefault() {
assertTrue(privacyItemController.micCameraAvailable)
}
@@ -111,10 +103,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
executor.runAllReady()
verify(callback).onFlagMicCameraChanged(false)
- verify(callback, never()).onFlagAllChanged(anyBoolean())
assertFalse(privacyItemController.micCameraAvailable)
- assertFalse(privacyItemController.allIndicatorsAvailable)
}
@Test
@@ -127,26 +117,15 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testAllChanged() {
- changeAll(true)
- executor.runAllReady()
-
- verify(callback).onFlagAllChanged(true)
- verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
-
- assertTrue(privacyItemController.allIndicatorsAvailable)
- }
-
- @Test
fun testBothChanged() {
changeAll(true)
changeMicCamera(false)
executor.runAllReady()
- verify(callback, atLeastOnce()).onFlagAllChanged(true)
+ verify(callback, atLeastOnce()).onFlagLocationChanged(true)
verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
- assertTrue(privacyItemController.allIndicatorsAvailable)
+ assertTrue(privacyItemController.locationAvailable)
assertFalse(privacyItemController.micCameraAvailable)
}
@@ -186,28 +165,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testSomeListening_stillListening() {
- // Mic and camera are true by default
- changeAll(true)
- executor.runAllReady()
- changeAll(false)
- executor.runAllReady()
-
- verify(appOpsController, never()).removeCallback(any(), any())
- }
-
- @Test
- fun testAllDeleted_micCameraFalse_stopListening() {
- changeMicCamera(false)
- changeAll(true)
- executor.runAllReady()
- changeAll(null)
- executor.runAllReady()
-
- verify(appOpsController).removeCallback(any(), any())
- }
-
- @Test
fun testMicDeleted_stillListening() {
changeMicCamera(true)
executor.runAllReady()
@@ -219,7 +176,10 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
- private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+ private fun changeAll(value: Boolean?) {
+ changeMicCamera(value)
+ changeLocation(value)
+ }
private fun changeProperty(name: String, value: Boolean?) {
deviceConfigProxy.setProperty(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 7ca468edfd9c..b87c7a6ad2d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -43,7 +43,6 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Assert.assertTrue
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -72,8 +71,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
const val TEST_PACKAGE_NAME = "test"
- private const val ALL_INDICATORS =
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+ private const val LOCATION_INDICATOR =
+ SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
@@ -119,7 +118,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
deviceConfigProxy = DeviceConfigProxyFake()
// Listen to everything by default
- changeAll(true)
+ changeMicCamera(true)
+ changeLocation(true)
`when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0)))
@@ -259,9 +259,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
}
@Test
- @Ignore // TODO(b/168209929)
fun testNotListeningWhenIndicatorsDisabled() {
- changeAll(false)
+ changeLocation(false)
changeMicCamera(false)
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -271,7 +270,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
@Test
fun testNotSendingLocationWhenOnlyMicCamera() {
- changeAll(false)
+ changeLocation(false)
changeMicCamera(true)
executor.runAllReady()
@@ -294,7 +293,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
privacyItemController.addCallback(callback)
- changeAll(false)
+ changeLocation(false)
changeMicCamera(true)
executor.runAllReady()
reset(callback) // Clean callback
@@ -521,7 +520,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
}
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
- private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+ private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value)
private fun changeProperty(name: String, value: Boolean?) {
deviceConfigProxy.setProperty(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 862e3747f602..5422ae831de3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -14,6 +14,9 @@
package com.android.systemui.qs;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -24,6 +27,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.DialogInterface;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
@@ -74,6 +79,8 @@ public class QSSecurityFooterTest extends SysuiTestCase {
private final String VPN_PACKAGE = "TestVPN";
private final String VPN_PACKAGE_2 = "TestVPN 2";
private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App";
+ private static final ComponentName DEVICE_OWNER_COMPONENT =
+ new ComponentName("TestDPC", "Test");
private ViewGroup mRootView;
private TextView mFooterText;
@@ -101,6 +108,11 @@ public class QSSecurityFooterTest extends SysuiTestCase {
mFooterIcon = mRootView.findViewById(R.id.footer_icon);
mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
mFooter.setHostEnvironment(null);
+
+ when(mSecurityController.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
}
@Test
@@ -148,6 +160,27 @@ public class QSSecurityFooterTest extends SysuiTestCase {
}
@Test
+ public void testManagedFinancedDeviceWithOwnerName() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ mFooter.refreshState();
+
+ TestableLooper.get(this).processAllMessages();
+ assertEquals(mContext.getString(
+ R.string.quick_settings_financed_disclosure_named_management,
+ MANAGING_ORGANIZATION), mFooterText.getText());
+ assertEquals(View.VISIBLE, mRootView.getVisibility());
+ assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+ assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
+ // -1 == never set.
+ assertEquals(-1, mFooterIcon.getLastImageResource());
+ }
+
+ @Test
public void testManagedDemoMode() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerOrganizationName()).thenReturn(null);
@@ -383,6 +416,25 @@ public class QSSecurityFooterTest extends SysuiTestCase {
}
@Test
+ public void testGetManagementTitleForNonFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+
+ assertEquals(mContext.getString(R.string.monitoring_title_device_owned),
+ mFooter.getManagementTitle(MANAGING_ORGANIZATION));
+ }
+
+ @Test
+ public void testGetManagementTitleForFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ assertEquals(mContext.getString(R.string.monitoring_title_financed_device,
+ MANAGING_ORGANIZATION),
+ mFooter.getManagementTitle(MANAGING_ORGANIZATION));
+ }
+
+ @Test
public void testGetManagementMessage_noManagement() {
assertEquals(null, mFooter.getManagementMessage(
/* isDeviceManaged= */ false,
@@ -409,6 +461,21 @@ public class QSSecurityFooterTest extends SysuiTestCase {
}
@Test
+ public void testGetManagementMessage_deviceOwner_asFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
+ MANAGING_ORGANIZATION, MANAGING_ORGANIZATION),
+ mFooter.getManagementMessage(
+ /* isDeviceManaged= */ true,
+ MANAGING_ORGANIZATION,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+ /* workProfileOrganizationName= */ null));
+ }
+
+ @Test
public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() {
assertEquals(mContext.getString(R.string.monitoring_description_named_management,
MANAGING_ORGANIZATION),
@@ -587,6 +654,34 @@ public class QSSecurityFooterTest extends SysuiTestCase {
assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
}
+ @Test
+ public void testCreateDialogViewForFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ // Initialize AlertDialog which sets the text for the negative button, which is used when
+ // creating the dialog for a financed device.
+ mFooter.showDeviceMonitoringDialog();
+ // The above statement would display the Quick Settings dialog which requires user input,
+ // so simulate the press to continue with the unit test (otherwise, it is stuck).
+ mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+ View view = mFooter.createDialogView();
+
+ TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle);
+ assertEquals(View.VISIBLE, managementSubtitle.getVisibility());
+ assertEquals(mContext.getString(R.string.monitoring_title_financed_device,
+ MANAGING_ORGANIZATION), managementSubtitle.getText());
+ TextView managementMessage = view.findViewById(R.id.device_management_warning);
+ assertEquals(View.VISIBLE, managementMessage.getVisibility());
+ assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
+ MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), managementMessage.getText());
+ assertEquals(mContext.getString(R.string.monitoring_button_view_policies),
+ mFooter.getSettingsButton());
+ }
+
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index cfef5beb94e0..2ca8082f777a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -49,6 +49,7 @@ import com.android.internal.util.CollectionUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSFactory;
@@ -360,6 +361,7 @@ public class QSTileHostTest extends SysuiTestCase {
host,
mLooper.getLooper(),
new Handler(mLooper.getLooper()),
+ new FalsingManagerFake(),
mock(MetricsLogger.class),
mock(StatusBarStateController.class),
mock(ActivityStarter.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 97a845916185..4948c2b18746 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -148,7 +148,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_noIndicators() {
- setPrivacyController(false, false, false)
+ setPrivacyController(micCamera = false, location = false)
controller.init()
@@ -160,7 +160,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_onlyMicCamera() {
- setPrivacyController(false, true, false)
+ setPrivacyController(micCamera = true, location = false)
controller.init()
@@ -177,7 +177,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_onlyLocation() {
- setPrivacyController(false, false, true)
+ setPrivacyController(micCamera = false, location = true)
controller.init()
@@ -192,26 +192,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_locationMicCamera() {
- setPrivacyController(false, true, true)
-
- controller.init()
-
- val captor = argumentCaptor<List<String>>()
- verify(iconContainer).setIgnoredSlots(capture(captor))
-
- val cameraString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_camera)
- val micString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_microphone)
- val locationString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_location)
-
- assertThat(captor.value).containsExactly(cameraString, micString, locationString)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_all() {
- setPrivacyController(true, false, false)
+ setPrivacyController(micCamera = true, location = true)
controller.init()
@@ -248,8 +229,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
}
- private fun setPrivacyController(all: Boolean, micCamera: Boolean, location: Boolean) {
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(all)
+ private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
`when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
`when`(privacyItemController.locationAvailable).thenReturn(location)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 3aa40dec1fad..b1c3d1da8fea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -32,6 +32,7 @@ import android.testing.TestableLooper
import android.view.IWindowManager
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -103,6 +104,7 @@ class CustomTileTest : SysuiTestCase() {
{ tileHost },
testableLooper.looper,
Handler(testableLooper.looper),
+ FalsingManagerFake(),
metricsLogger,
statusBarStateController,
activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 61a0d6c17eed..937ab1c5c41d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -26,6 +26,8 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -55,7 +57,9 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSEvent;
@@ -90,6 +94,7 @@ public class QSTileImplTest extends SysuiTestCase {
private QSTileHost mHost;
@Mock
private MetricsLogger mMetricsLogger;
+ private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -112,7 +117,7 @@ public class QSTileImplTest extends SysuiTestCase {
Handler mainHandler = new Handler(mTestableLooper.getLooper());
- mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler,
+ mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager,
mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
mTile.setTileSpec(SPEC);
}
@@ -144,6 +149,19 @@ public class QSTileImplTest extends SysuiTestCase {
}
@Test
+ public void testClick_falsing() {
+ mFalsingManager.setFalseRobustTap(true);
+ mTile.click();
+ mTestableLooper.processAllMessages();
+ assertThat(mTile.mClicked).isFalse();
+
+ mFalsingManager.setFalseRobustTap(false);
+ mTile.click();
+ mTestableLooper.processAllMessages();
+ assertThat(mTile.mClicked).isTrue();
+ }
+
+ @Test
public void testSecondaryClick_Metrics() {
mTile.secondaryClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
@@ -360,17 +378,20 @@ public class QSTileImplTest extends SysuiTestCase {
}
private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
+ boolean mClicked;
+
protected TileImpl(
QSHost host,
Looper backgroundLooper,
Handler mainHandler,
+ FalsingManager falsingManager,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger
) {
- super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
- activityStarter, qsLogger);
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
getState().state = Tile.STATE_ACTIVE;
}
@@ -381,7 +402,7 @@ public class QSTileImplTest extends SysuiTestCase {
@Override
protected void handleClick() {
-
+ mClicked = true;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
new file mode 100644
index 000000000000..9674a60c1ac5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -0,0 +1,149 @@
+package com.android.systemui.qs.tiles
+
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.os.Handler
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.NextAlarmController
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AlarmTileTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var qsHost: QSHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var nextAlarmController: NextAlarmController
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var pendingIntent: PendingIntent
+ @Captor
+ private lateinit var callbackCaptor: ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: AlarmTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ `when`(qsHost.context).thenReturn(mContext)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile = AlarmTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ featureFlags,
+ userTracker,
+ nextAlarmController
+ )
+
+ verify(nextAlarmController).observe(eq(tile), capture(callbackCaptor))
+ tile.refreshState()
+ testableLooper.processAllMessages()
+ }
+
+ @Test
+ fun testNotAvailableFeatureFlag() {
+ `when`(featureFlags.isAlarmTileAvailable).thenReturn(false)
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testAvailableFeatureFlag() {
+ `when`(featureFlags.isAlarmTileAvailable).thenReturn(true)
+ assertThat(tile.isAvailable).isTrue()
+ }
+
+ @Test
+ fun testDoesntHandleLongClick() {
+ assertThat(tile.state.handlesLongClick).isFalse()
+ }
+
+ @Test
+ fun testInactiveByDefault() {
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun testInactiveAfterNullNextAlarm() {
+ callbackCaptor.value.onNextAlarmChanged(null)
+
+ testableLooper.processAllMessages()
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun testActivityStartedWhenNullNextAlarm() {
+ callbackCaptor.value.onNextAlarmChanged(null)
+ tile.click()
+
+ testableLooper.processAllMessages()
+ verify(activityStarter).postStartActivityDismissingKeyguard(tile.defaultIntent, 0)
+ }
+
+ @Test
+ fun testActiveAfterNextAlarm() {
+ val alarmInfo = AlarmManager.AlarmClockInfo(1L, pendingIntent)
+ callbackCaptor.value.onNextAlarmChanged(alarmInfo)
+
+ testableLooper.processAllMessages()
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ }
+
+ @Test
+ fun testActivityStartedWhenNextAlarm() {
+ val alarmInfo = AlarmManager.AlarmClockInfo(1L, pendingIntent)
+ callbackCaptor.value.onNextAlarmChanged(alarmInfo)
+ tile.click()
+
+ testableLooper.processAllMessages()
+ verify(activityStarter).postStartActivityDismissingKeyguard(pendingIntent)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index bcfc83570345..f17bd56d0052 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -24,6 +24,7 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
@@ -79,6 +80,7 @@ class BatterySaverTileTest : SysuiTestCase() {
qsHost,
testableLooper.looper,
Handler(testableLooper.looper),
+ FalsingManagerFake(),
metricsLogger,
statusBarStateController,
activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 1c29a8174359..7d393610c2b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
@@ -101,6 +102,7 @@ public class CastTileTest extends SysuiTestCase {
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index ccd9548b269f..9fe568718908 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.content.Context
+import android.content.Intent
import android.provider.Settings
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
@@ -26,11 +28,11 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -50,7 +52,9 @@ import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.doNothing
import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.Optional
@@ -80,8 +84,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
private lateinit var controlsController: ControlsController
@Mock
private lateinit var featureFlags: FeatureFlags
- @Mock
- private lateinit var controlsDialog: ControlsDialog
private lateinit var globalSettings: GlobalSettings
@Mock
private lateinit var serviceInfo: ControlsServiceInfo
@@ -95,6 +97,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
private lateinit var tile: DeviceControlsTile
private lateinit var secureSettings: SecureSettings
+ private lateinit var spiedContext: Context
private var featureEnabled = true
@Before
@@ -103,7 +106,9 @@ class DeviceControlsTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
- `when`(qsHost.context).thenReturn(mContext)
+ spiedContext = spy(mContext)
+ doNothing().`when`(spiedContext).startActivity(any(Intent::class.java))
+ `when`(qsHost.context).thenReturn(spiedContext)
`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(controlsController.available).thenReturn(true)
`when`(controlsComponent.isEnabled()).thenReturn(true)
@@ -276,7 +281,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
tile.click()
testableLooper.processAllMessages()
- verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
+ verify(spiedContext, never()).startActivity(any(Intent::class.java))
}
@Test
@@ -293,7 +298,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
tile.click()
testableLooper.processAllMessages()
- verify(controlsDialog).show(controlsUiController)
+ verify(spiedContext).startActivity(any(Intent::class.java))
}
@Test
@@ -311,25 +316,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
tile.click()
testableLooper.processAllMessages()
- verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
- }
-
- @Test
- fun testDialogDismissedOnDestroy() {
- verify(controlsListingController).observe(
- any(LifecycleOwner::class.java),
- capture(listingCallbackCaptor)
- )
-
- listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
- testableLooper.processAllMessages()
-
- tile.click()
- testableLooper.processAllMessages()
-
- tile.destroy()
- testableLooper.processAllMessages()
- verify(controlsDialog).dismiss()
+ verify(spiedContext, never()).startActivity(any(Intent::class.java))
}
private fun createTile(): DeviceControlsTile {
@@ -337,13 +324,13 @@ class DeviceControlsTileTest : SysuiTestCase() {
qsHost,
testableLooper.looper,
Handler(testableLooper.looper),
+ FalsingManagerFake(),
metricsLogger,
statusBarStateController,
activityStarter,
qsLogger,
controlsComponent,
featureFlags,
- { controlsDialog },
globalSettings
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index b37ac4a2b759..99d028cd8c5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -33,6 +33,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
@@ -84,6 +85,7 @@ public class NfcTileTest extends SysuiTestCase {
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
new file mode 100644
index 000000000000..33166ccadc27
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.content.pm.PackageManager.FEATURE_NFC_HOST_CARD_EMULATION;
+import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quicksettings.Tile;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class QuickAccessWalletTileTest extends SysuiTestCase {
+
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSLogger mQSLogger;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
+ @Mock
+ private QuickAccessWalletClient mQuickAccessWalletClient;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private SecureSettings mSecureSettings;
+ @Mock
+ private FeatureFlags mFeatureFlags;
+
+ private TestableLooper mTestableLooper;
+ private QuickAccessWalletTile mTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getContext()).thenReturn(mContext);
+ when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
+ when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true);
+
+ mTile = new QuickAccessWalletTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mQuickAccessWalletClient,
+ mKeyguardStateController,
+ mPackageManager,
+ mSecureSettings,
+ mFeatureFlags);
+ }
+
+ @Test
+ public void testNewTile() {
+ assertFalse(mTile.newTileState().handlesLongClick);
+ }
+
+ @Test
+ public void testIsAvailable_featureFlagIsOff() {
+ when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(false);
+ assertFalse(mTile.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_qawServiceNotAvailable() {
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+ assertFalse(mTile.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_qawServiceAvailable() {
+ when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false);
+ when(mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT)).thenReturn("Component");
+
+ assertTrue(mTile.isAvailable());
+ }
+
+ @Test
+ public void testHandleClick_openGPay() {
+ Intent intent = new Intent("WalletIntent");
+ when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(intent);
+ mTile.handleClick();
+
+ verify(mActivityStarter, times(1))
+ .postStartActivityDismissingKeyguard(eq(intent), anyInt());
+ }
+
+ @Test
+ public void testHandleUpdateState_updateLabelAndIcon() {
+ QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet);
+ QSTile.State state = new QSTile.State();
+ when(mQuickAccessWalletClient.getServiceLabel()).thenReturn("QuickAccessWallet");
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals("QuickAccessWallet", state.label.toString());
+ assertTrue(state.label.toString().contentEquals(state.contentDescription));
+ assertEquals(icon, state.icon);
+ }
+
+ @Test
+ public void testHandleUpdateState_deviceLocked_tileInactive() {
+ QSTile.State state = new QSTile.State();
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals(Tile.STATE_INACTIVE, state.state);
+ assertNull(state.stateDescription);
+ }
+
+ @Test
+ public void testHandleUpdateState_deviceLocked_tileActive() {
+ QSTile.State state = new QSTile.State();
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals(Tile.STATE_ACTIVE, state.state);
+ assertTrue(state.secondaryLabel.toString().contentEquals(state.stateDescription));
+ assertEquals(
+ getContext().getString(R.string.wallet_secondary_label),
+ state.secondaryLabel.toString());
+ }
+
+ @Test
+ public void testHandleUpdateState_qawFeatureUnavailable_tileUnavailable() {
+ QSTile.State state = new QSTile.State();
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false);
+
+ mTile.handleUpdateState(state, new Object());
+
+ assertEquals(Tile.STATE_UNAVAILABLE, state.state);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 880c290802df..6032e51ab554 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
@@ -82,6 +83,7 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 6b54791dd143..22154332c953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
@@ -81,6 +82,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
index 9e62a6263a43..63f7c9755782 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
@@ -24,6 +24,7 @@ import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.view.IScrollCaptureCallbacks;
@@ -37,19 +38,15 @@ import android.view.Surface;
class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
private final int[] mColors = {Color.RED, Color.GREEN, Color.BLUE};
private IScrollCaptureCallbacks mCallbacks;
- private Surface mSurface;
private Paint mPaint;
private int mNextColor;
private HwuiContext mHwuiContext;
-
- FakeScrollCaptureConnection(IScrollCaptureCallbacks cb) {
- mCallbacks = cb;
- }
+ private CancellationSignal mCancellationSignal;
@Override
- public ICancellationSignal startCapture(Surface surface) {
- mSurface = surface;
- mHwuiContext = new HwuiContext(false, surface);
+ public ICancellationSignal startCapture(Surface surface, IScrollCaptureCallbacks callbacks) {
+ mCallbacks = callbacks;
+ mHwuiContext = new HwuiContext(surface);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
try {
@@ -57,7 +54,9 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- return null;
+ ICancellationSignal signal = CancellationSignal.createTransport();
+ mCancellationSignal = CancellationSignal.fromTransport(signal);
+ return signal;
}
@Override
@@ -72,7 +71,9 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- return null;
+ ICancellationSignal signal = CancellationSignal.createTransport();
+ mCancellationSignal = CancellationSignal.fromTransport(signal);
+ return signal;
}
@Override
@@ -83,15 +84,15 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
e.rethrowAsRuntimeException();
} finally {
mHwuiContext.destroy();
- mSurface = null;
mCallbacks = null;
}
- return null;
+ ICancellationSignal signal = CancellationSignal.createTransport();
+ mCancellationSignal = CancellationSignal.fromTransport(signal);
+ return signal;
}
@Override
public void close() throws RemoteException {
-
}
// From android.view.Surface, but issues render requests synchronously with waitForPresent(true)
@@ -99,21 +100,16 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
private final RenderNode mRenderNode;
private final HardwareRenderer mHardwareRenderer;
private RecordingCanvas mCanvas;
- private final boolean mIsWideColorGamut;
- HwuiContext(boolean isWideColorGamut, Surface surface) {
+ HwuiContext(Surface surface) {
mRenderNode = RenderNode.create("HwuiCanvas", null);
mRenderNode.setClipToBounds(false);
mRenderNode.setForceDarkAllowed(false);
- mIsWideColorGamut = isWideColorGamut;
mHardwareRenderer = new HardwareRenderer();
mHardwareRenderer.setContentRoot(mRenderNode);
mHardwareRenderer.setSurface(surface, true);
- mHardwareRenderer.setColorMode(
- isWideColorGamut
- ? ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
- : ActivityInfo.COLOR_MODE_DEFAULT);
+ mHardwareRenderer.setColorMode(ActivityInfo.COLOR_MODE_DEFAULT);
mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f);
mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f);
}
@@ -142,9 +138,5 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
void destroy() {
mHardwareRenderer.destroy();
}
-
- boolean isWideColorGamut() {
- return mIsWideColorGamut;
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 802b462ec10e..cf7dc20160ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -16,15 +16,15 @@
package com.android.systemui.screenshot;
-import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
import static java.util.Objects.requireNonNull;
@@ -34,7 +34,7 @@ import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.view.Display;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
@@ -43,30 +43,29 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.google.common.util.concurrent.ListenableFuture;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import org.mockito.stubbing.Answer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ScrollCaptureClientTest extends SysuiTestCase {
- private static final float MAX_PAGES = 3f;
+ private static final float MAX_PAGES = 3.0f;
private Context mContext;
private IWindowManager mWm;
- @Spy private TestableConsumer<Session> mSessionConsumer;
- @Spy private TestableConsumer<Connection> mConnectionConsumer;
- @Spy private TestableConsumer<CaptureResult> mResultConsumer;
- @Mock private Runnable mRunnable;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -79,46 +78,50 @@ public class ScrollCaptureClientTest extends SysuiTestCase {
}
@Test
- public void testBasicClientFlow() throws RemoteException {
+ public void testDetectAndConnect()
+ throws RemoteException, InterruptedException, ExecutionException, TimeoutException {
doAnswer((Answer<Void>) invocation -> {
- IScrollCaptureCallbacks cb = invocation.getArgument(3);
- cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder()
+ IScrollCaptureResponseListener listener = invocation.getArgument(3);
+ listener.onScrollCaptureResponse(new ScrollCaptureResponse.Builder()
.setBoundsInWindow(new Rect(0, 0, 100, 100))
.setWindowBounds(new Rect(0, 0, 100, 100))
- .setConnection(new FakeScrollCaptureConnection(cb))
+ .setConnection(new FakeScrollCaptureConnection())
.build());
return null;
}).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(),
- /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class));
+ /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class));
// Create client
ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
- client.request(Display.DEFAULT_DISPLAY, mConnectionConsumer);
- verify(mConnectionConsumer, timeout(100)).accept(any(Connection.class));
+ // Request scroll capture
+ ListenableFuture<ScrollCaptureResponse> requestFuture =
+ client.request(Display.DEFAULT_DISPLAY);
+ assertNotNull(requestFuture.get(100, TimeUnit.MILLISECONDS));
- Connection conn = mConnectionConsumer.getValue();
+ ScrollCaptureResponse response = requestFuture.get();
+ assertTrue(response.isConnected());
- conn.start(mSessionConsumer, MAX_PAGES);
- verify(mSessionConsumer, timeout(100)).accept(any(Session.class));
+ // Start a session
+ ListenableFuture<Session> startFuture = client.start(response, MAX_PAGES);
+ assertNotNull(startFuture.get(100, TimeUnit.MILLISECONDS));
- Session session = mSessionConsumer.getValue();
+ Session session = startFuture.get();
Rect request = new Rect(0, 0, session.getPageWidth(), session.getTileHeight());
- session.requestTile(0, mResultConsumer);
- verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class));
+ // Request a tile
+ ListenableFuture<CaptureResult> tileFuture = session.requestTile(0);
+ assertNotNull(tileFuture.get(100, TimeUnit.MILLISECONDS));
- CaptureResult result = mResultConsumer.getValue();
- assertThat(result.requested).isEqualTo(request);
- assertThat(result.captured).isEqualTo(result.requested);
- assertThat(result.image).isNotNull();
+ CaptureResult result = tileFuture.get();
+ assertEquals(request, result.requested);
+ assertEquals(result.requested, result.captured);
+ assertNotNull(result.image);
- session.end(mRunnable);
- verify(mRunnable, timeout(100)).run();
-
- // TODO verify image
- // TODO test threading
- // TODO test failures
+ // End the session
+ ListenableFuture<Void> endFuture = session.end();
+ CountDownLatch latch = new CountDownLatch(1);
+ endFuture.addListener(latch::countDown, Runnable::run);
+ assertTrue(latch.await(100, TimeUnit.MILLISECONDS));
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
index 6564d588f4ea..06b39abd1d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
@@ -16,15 +16,16 @@
package com.android.systemui.screenshot;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Intent;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.util.Log;
import android.view.Display;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
import android.view.WindowManagerGlobal;
@@ -45,8 +46,9 @@ import java.util.concurrent.TimeUnit;
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class ScrollCaptureTest extends SysuiTestCase {
- private static final String TAG = "ScrollCaptureTest";
+public class ScrollCaptureFrameworkSmokeTest extends SysuiTestCase {
+ private static final String TAG = "ScrollCaptureFrameworkSmokeTest";
+ private volatile ScrollCaptureResponse mResponse;
/**
* Verifies that a request traverses from SystemUI, to WindowManager and to the app process and
@@ -64,34 +66,23 @@ public class ScrollCaptureTest extends SysuiTestCase {
final CountDownLatch latch = new CountDownLatch(1);
try {
wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
- new IScrollCaptureCallbacks.Stub() {
+ new IScrollCaptureResponseListener.Stub() {
@Override
- public void onScrollCaptureResponse(ScrollCaptureResponse response)
+ public void onScrollCaptureResponse(
+ ScrollCaptureResponse response)
throws RemoteException {
- Log.d(TAG, "onScrollCaptureResponse: " + response);
+ mResponse = response;
latch.countDown();
}
-
- @Override
- public void onCaptureStarted() {
- }
-
- @Override
- public void onImageRequestCompleted(int i, Rect rect)
- throws RemoteException {
-
- }
-
- @Override
- public void onCaptureEnded() throws RemoteException {
-
- }
-
});
} catch (RemoteException e) {
Log.e(TAG, "request failed", e);
fail("caught remote exception " + e);
}
latch.await(1000, TimeUnit.MILLISECONDS);
+
+ assertNotNull(mResponse);
+ assertTrue(mResponse.isConnected());
+ assertTrue(mResponse.getWindowTitle().contains(ScrollViewActivity.class.getSimpleName()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 2917dfafd6a6..8ec03d76cfea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -476,4 +476,11 @@ public class CommandQueueTest extends SysuiTestCase {
waitForIdleSync();
verify(mCallbacks).requestWindowMagnificationConnection(true);
}
+
+ @Test
+ public void testSetEnableNavigationBarLumaSampling() {
+ mCommandQueue.setNavigationBarLumaSamplingEnabled(1, true);
+ waitForIdleSync();
+ verify(mCallbacks).setNavigationBarLumaSamplingEnabled(eq(1), eq(true));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 14877eec9a83..30708a7cb2fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -127,25 +127,7 @@ public class HighPriorityProviderTest extends SysuiTestCase {
.getPeopleNotificationType(entry))
.thenReturn(TYPE_NON_PERSON);
- // THEN it has high priority
- assertTrue(mHighPriorityProvider.isHighPriority(entry));
- }
-
- @Test
- public void minImportanceForeground() {
- // GIVEN notification is low importance and is associated with a foreground service
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_MIN)
- .build();
- when(mPeopleNotificationIdentifier
- .getPeopleNotificationType(entry))
- .thenReturn(TYPE_NON_PERSON);
-
- // THEN it does NOT have high priority
+ // THEN it has low priority
assertFalse(mHighPriorityProvider.isHighPriority(entry));
}
@@ -155,7 +137,6 @@ public class HighPriorityProviderTest extends SysuiTestCase {
// to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
final Notification notification = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
.build();
final NotificationChannel channel = new NotificationChannel("a", "a",
IMPORTANCE_LOW);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 97313bafb8a8..950b95f41542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -46,6 +46,7 @@ import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -260,6 +261,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mGutsManager,
true,
null,
+ new FalsingManagerFake(),
new FalsingCollectorFake(),
mPeopleNotificationIdentifier,
Optional.of(mock(BubblesManager.class))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index f3813fcaf6fc..b4a3393ab5db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -45,6 +45,7 @@ import android.widget.RemoteViews;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -429,6 +430,7 @@ public class NotificationTestHelper {
mock(OnExpandClickListener.class),
mock(NotificationMediaManager.class),
mock(ExpandableNotificationRow.CoordinateOnClickListener.class),
+ new FalsingManagerFake(),
new FalsingCollectorFake(),
mStatusBarStateController,
mPeopleNotificationIdentifier,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 8cd71031a8f8..c1d2ea88a1b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -322,23 +322,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
}
@Test
- public void testPeopleFiltering_addHeadersFromShowingOnlyGentle() {
- enablePeopleFiltering();
-
- setStackState(
- GENTLE_HEADER,
- PERSON,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- verify(mNssl).changeViewPosition(mSectionsManager.getSilentHeaderView(), 2);
- verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
- verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
- }
-
- @Test
- public void testPeopleFiltering_addAllHeaders() {
+ public void testPeopleFiltering_onlyAddSilentHeader() {
enablePeopleFiltering();
setStackState(
@@ -348,26 +332,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 2);
- verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
- verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
- }
-
- @Test
- public void testPeopleFiltering_moveAllHeaders() {
- enablePeopleFiltering();
-
- setStackState(
- PEOPLE_HEADER,
- ALERTING_HEADER,
- GENTLE_HEADER,
- PERSON,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- verify(mNssl).changeViewPosition(mSectionsManager.getSilentHeaderView(), 4);
- verify(mNssl).changeViewPosition(mSectionsManager.getAlertingHeaderView(), 2);
- verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
}
@Test
@@ -385,9 +349,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.INCOMING_HEADER,
ChildType.HEADS_UP,
- ChildType.PEOPLE_HEADER,
ChildType.PERSON,
ChildType.GENTLE_HEADER,
ChildType.GENTLE
@@ -408,10 +370,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.INCOMING_HEADER,
ChildType.HEADS_UP,
ChildType.HEADS_UP,
- ChildType.PEOPLE_HEADER,
ChildType.PERSON
);
}
@@ -428,7 +388,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.PEOPLE_HEADER,
ChildType.PERSON,
ChildType.PERSON
);
@@ -444,9 +403,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
);
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.INCOMING_HEADER,
ChildType.HEADS_UP,
- ChildType.PEOPLE_HEADER,
ChildType.PERSON
);
}
@@ -467,12 +424,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.INCOMING_HEADER,
ChildType.HEADS_UP,
ChildType.FSN,
- ChildType.PEOPLE_HEADER,
ChildType.PERSON,
- ChildType.ALERTING_HEADER,
ChildType.ALERTING,
ChildType.GENTLE_HEADER,
ChildType.GENTLE
@@ -517,7 +471,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
}
@Test
- public void testRemoveIncomingHeader() {
+ public void testRemoveNonSilentHeader() {
enablePeopleFiltering();
enableMediaControls();
@@ -539,9 +493,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
verifyMockStack(
ChildType.MEDIA_CONTROLS,
- ChildType.PEOPLE_HEADER,
ChildType.PERSON,
- ChildType.ALERTING_HEADER,
ChildType.ALERTING,
ChildType.ALERTING,
ChildType.ALERTING,
@@ -569,13 +521,10 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.INCOMING_HEADER,
ChildType.HEADS_UP,
ChildType.HEADS_UP,
ChildType.HEADS_UP,
- ChildType.PEOPLE_HEADER,
ChildType.PERSON,
- ChildType.ALERTING_HEADER,
ChildType.ALERTING
);
}
@@ -593,7 +542,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.updateSectionBoundaries();
verifyMockStack(
- ChildType.ALERTING_HEADER,
ChildType.PERSON,
ChildType.ALERTING,
ChildType.GENTLE_HEADER,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index bdde82289e86..8b5ba3848500 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -174,6 +174,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
DozeLog.PULSE_REASON_DOCKING,
DozeLog.REASON_SENSOR_WAKE_UP,
+ DozeLog.REASON_SENSOR_QUICK_PICKUP,
DozeLog.REASON_SENSOR_TAP));
HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index b1f1b5e78b5c..116e1b98debd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -917,11 +917,11 @@ public class ScrimControllerTest extends SysuiTestCase {
HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
- ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED));
+ ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
for (ScrimState state : ScrimState.values()) {
if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
- Assert.fail("Scrim state not whitelisted nor blacklisted as low power mode");
+ Assert.fail("Scrim state isn't categorized as a low power or regular state.");
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index a844d099d43a..a60baa54541e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -45,6 +45,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -86,6 +87,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
mRemoteInputQuickSettingsDisabler);
mDependency.injectTestDependency(LightBarController.class,
mLightBarController);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.class);
mReceiver = new BlockingQueueIntentReceiver();
mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 44184ee8eeed..ed87a4040022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -62,6 +64,9 @@ import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SecurityControllerTest extends SysuiTestCase {
+ private static final ComponentName DEVICE_OWNER_COMPONENT =
+ new ComponentName("com.android.foo", "bar");
+
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class);
private final UserManager mUserManager = mock(UserManager.class);
@@ -127,6 +132,22 @@ public class SecurityControllerTest extends SysuiTestCase {
}
@Test
+ public void testGetDeviceOwnerComponentOnAnyUser() {
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ assertEquals(mSecurityController.getDeviceOwnerComponentOnAnyUser(),
+ DEVICE_OWNER_COMPONENT);
+ }
+
+ @Test
+ public void testGetDeviceOwnerType() {
+ when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+ assertEquals(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT),
+ DEVICE_OWNER_TYPE_FINANCED);
+ }
+
+ @Test
public void testWorkAccount() throws Exception {
assertFalse(mSecurityController.hasCACertInCurrentUser());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index c0722a459929..3640bcd64c14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -15,6 +15,7 @@
package com.android.systemui.utils.leaks;
import android.app.admin.DeviceAdminInfo;
+import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.testing.LeakCheck;
@@ -68,6 +69,16 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa
}
@Override
+ public ComponentName getDeviceOwnerComponentOnAnyUser() {
+ return null;
+ }
+
+ @Override
+ public int getDeviceOwnerType(ComponentName admin) {
+ return 0;
+ }
+
+ @Override
public boolean isNetworkLoggingEnabled() {
return false;
}
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index f8b9309f9a7f..f0de81183919 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -360,7 +360,7 @@ public class ProxyServer extends Thread {
try {
mCallback.setProxyPort(port);
} catch (RemoteException e) {
- Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+ Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
}
}
mPort = port;
@@ -371,7 +371,7 @@ public class ProxyServer extends Thread {
try {
callback.setProxyPort(mPort);
} catch (RemoteException e) {
- Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+ Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
}
}
mCallback = callback;
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
index bdf478d36c8c..a8e26221a7ab 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
@@ -30,7 +30,7 @@ public class ProxyService extends Service {
private static ProxyServer server = null;
- /** Keep these values up-to-date with PacProxyInstaller.java */
+ /** Keep these values up-to-date with PacProxyService.java */
public static final String KEY_PROXY = "keyProxy";
public static final String HOST = "localhost";
public static final String EXCL_LIST = "";
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 5dd271c9dbb1..f06a94004110 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -340,5 +340,9 @@ message SystemMessage {
// Notify the user that window magnification is available.
// package: android
NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE = 1004;
+
+ // Notify the user that some accessibility service has view and control permissions.
+ // package: android
+ NOTE_A11Y_VIEW_AND_CONTROL_ACCESS = 1005;
}
}
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 238bf0f5a335..d420bd4bdb66 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -52,8 +52,12 @@ import android.view.Surface;
* <p>For more information about creating an application that uses RenderScript, read the
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
-
+@Deprecated
public class Allocation extends BaseObj {
private static final int MAX_NUMBER_IO_INPUT_ALLOC = 16;
diff --git a/rs/java/android/renderscript/AllocationAdapter.java b/rs/java/android/renderscript/AllocationAdapter.java
index 6d7e97ebb0fe..17bc23421894 100644
--- a/rs/java/android/renderscript/AllocationAdapter.java
+++ b/rs/java/android/renderscript/AllocationAdapter.java
@@ -19,7 +19,11 @@ package android.renderscript;
/**
* Only intended for use by generated reflected code.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class AllocationAdapter extends Allocation {
Type mWindow;
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index 7b5514b8a0d1..ea8535d6d621 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -27,7 +27,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* It is responsible for lifetime management and resource tracking. This class
* should not be used by a user application.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class BaseObj {
BaseObj(long id, RenderScript rs) {
rs.validate();
diff --git a/rs/java/android/renderscript/Byte2.java b/rs/java/android/renderscript/Byte2.java
index 3ad79e400c91..cb5cc473a48e 100644
--- a/rs/java/android/renderscript/Byte2.java
+++ b/rs/java/android/renderscript/Byte2.java
@@ -20,7 +20,11 @@ package android.renderscript;
/**
* Class for exposing the native RenderScript byte2 type back to the Android system.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Byte2 {
public byte x;
public byte y;
diff --git a/rs/java/android/renderscript/Byte3.java b/rs/java/android/renderscript/Byte3.java
index a138313321d0..aca4e645f102 100644
--- a/rs/java/android/renderscript/Byte3.java
+++ b/rs/java/android/renderscript/Byte3.java
@@ -20,7 +20,11 @@ package android.renderscript;
/**
* Class for exposing the native RenderScript byte3 type back to the Android system.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Byte3 {
public byte x;
public byte y;
diff --git a/rs/java/android/renderscript/Byte4.java b/rs/java/android/renderscript/Byte4.java
index fa4c13d79714..b30b6ed00d09 100644
--- a/rs/java/android/renderscript/Byte4.java
+++ b/rs/java/android/renderscript/Byte4.java
@@ -20,7 +20,11 @@ package android.renderscript;
/**
* Class for exposing the native RenderScript byte4 type back to the Android system.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Byte4 {
public byte x;
public byte y;
diff --git a/rs/java/android/renderscript/Double2.java b/rs/java/android/renderscript/Double2.java
index 4c7319d5a4b0..e14228a6f785 100644
--- a/rs/java/android/renderscript/Double2.java
+++ b/rs/java/android/renderscript/Double2.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic double type.
* Provides two double fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Double2 {
public double x;
public double y;
diff --git a/rs/java/android/renderscript/Double3.java b/rs/java/android/renderscript/Double3.java
index b819716017e9..e52c902a27fa 100644
--- a/rs/java/android/renderscript/Double3.java
+++ b/rs/java/android/renderscript/Double3.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic double type.
* Provides three double fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Double3 {
public double x;
public double y;
diff --git a/rs/java/android/renderscript/Double4.java b/rs/java/android/renderscript/Double4.java
index e4829f7426ae..a3e4a94af8f2 100644
--- a/rs/java/android/renderscript/Double4.java
+++ b/rs/java/android/renderscript/Double4.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic double type.
* Provides four double fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Double4 {
public double x;
public double y;
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 0941907d35f8..f671953f4704 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -51,7 +51,12 @@ import android.compat.annotation.UnsupportedAppUsage;
* <p>For more information about creating an application that uses RenderScript, read the
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Element extends BaseObj {
int mSize;
Element[] mElements;
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index de1c49730aaa..aaa0fe8d7e95 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -26,7 +26,11 @@ import java.util.BitSet;
* reflected code generated by the RS tool chain. It should not
* be called directly.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class FieldPacker {
public FieldPacker(int len) {
mPos = 0;
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 7cc2825ae565..f0a9fa718a6f 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -36,6 +36,7 @@ import java.io.InputStream;
* index entries for all the objects stored inside it.
*
**/
+@Deprecated
public class FileA3D extends BaseObj {
/**
diff --git a/rs/java/android/renderscript/Float2.java b/rs/java/android/renderscript/Float2.java
index e9f8ca7737ce..1f6038c9bfca 100644
--- a/rs/java/android/renderscript/Float2.java
+++ b/rs/java/android/renderscript/Float2.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic float type.
* Provides two float fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Float2 {
public float x;
public float y;
diff --git a/rs/java/android/renderscript/Float3.java b/rs/java/android/renderscript/Float3.java
index 555bdf6d6e4e..5f4571643daf 100644
--- a/rs/java/android/renderscript/Float3.java
+++ b/rs/java/android/renderscript/Float3.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic float type.
* Provides three float fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Float3 {
public float x;
public float y;
diff --git a/rs/java/android/renderscript/Float4.java b/rs/java/android/renderscript/Float4.java
index 6541b2ec7264..7f3ba2c5fc33 100644
--- a/rs/java/android/renderscript/Float4.java
+++ b/rs/java/android/renderscript/Float4.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic float type.
* Provides four float fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Float4 {
public float x;
public float y;
diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index e47ec4b31700..6f6f341d78a6 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -45,6 +45,7 @@ import java.util.Map;
* them in the script to suit the user's rendering needs. Font colors work as a state machine.
* Every new call to draw text uses the last color set in the script.</p>
**/
+@Deprecated
public class Font extends BaseObj {
//These help us create a font by family name
diff --git a/rs/java/android/renderscript/Int2.java b/rs/java/android/renderscript/Int2.java
index 120957bcd726..be0639f7137d 100644
--- a/rs/java/android/renderscript/Int2.java
+++ b/rs/java/android/renderscript/Int2.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic int type.
* Provides two int fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Int2 {
public int x;
public int y;
diff --git a/rs/java/android/renderscript/Int3.java b/rs/java/android/renderscript/Int3.java
index 5431b9a75ba7..38a602d6bb4e 100644
--- a/rs/java/android/renderscript/Int3.java
+++ b/rs/java/android/renderscript/Int3.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic int type.
* Provides three int fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Int3 {
public int x;
public int y;
diff --git a/rs/java/android/renderscript/Int4.java b/rs/java/android/renderscript/Int4.java
index 1c0e2e2621aa..52f7bb2c2461 100644
--- a/rs/java/android/renderscript/Int4.java
+++ b/rs/java/android/renderscript/Int4.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic int type.
* Provides four int fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Int4 {
public int x;
public int y;
diff --git a/rs/java/android/renderscript/Long2.java b/rs/java/android/renderscript/Long2.java
index fabf2046a48c..1b3955b04798 100644
--- a/rs/java/android/renderscript/Long2.java
+++ b/rs/java/android/renderscript/Long2.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic long type.
* Provides two long fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Long2 {
public long x;
public long y;
diff --git a/rs/java/android/renderscript/Long3.java b/rs/java/android/renderscript/Long3.java
index 8e243cce647f..8be9c1cf2774 100644
--- a/rs/java/android/renderscript/Long3.java
+++ b/rs/java/android/renderscript/Long3.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic long type.
* Provides three long fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Long3 {
public long x;
public long y;
diff --git a/rs/java/android/renderscript/Long4.java b/rs/java/android/renderscript/Long4.java
index 1a1ad748e462..75db51b1237a 100644
--- a/rs/java/android/renderscript/Long4.java
+++ b/rs/java/android/renderscript/Long4.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic long type.
* Provides four long fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Long4 {
public long x;
public long y;
diff --git a/rs/java/android/renderscript/Matrix2f.java b/rs/java/android/renderscript/Matrix2f.java
index 048262dc7eba..5f5e709d2c99 100644
--- a/rs/java/android/renderscript/Matrix2f.java
+++ b/rs/java/android/renderscript/Matrix2f.java
@@ -20,7 +20,11 @@ package android.renderscript;
/**
* Class for exposing the native RenderScript rs_matrix2x2 type back to the Android system.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Matrix2f {
/**
diff --git a/rs/java/android/renderscript/Matrix3f.java b/rs/java/android/renderscript/Matrix3f.java
index 9a4af777583c..b620eaf1415d 100644
--- a/rs/java/android/renderscript/Matrix3f.java
+++ b/rs/java/android/renderscript/Matrix3f.java
@@ -20,7 +20,11 @@ package android.renderscript;
/**
* Class for exposing the native RenderScript rs_matrix3x3 type back to the Android system.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Matrix3f {
/**
diff --git a/rs/java/android/renderscript/Matrix4f.java b/rs/java/android/renderscript/Matrix4f.java
index a9469c979494..cdf06a63c59f 100644
--- a/rs/java/android/renderscript/Matrix4f.java
+++ b/rs/java/android/renderscript/Matrix4f.java
@@ -22,7 +22,11 @@ import android.compat.annotation.UnsupportedAppUsage;
/**
* Class for exposing the native RenderScript rs_matrix4x4 type back to the Android system.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Matrix4f {
/**
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 1a4d1fd5afbb..f2fd5a92afe8 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -40,6 +40,7 @@ import java.util.Vector;
* index sets or primitive types.
* </p>
**/
+@Deprecated
public class Mesh extends BaseObj {
/**
diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java
index ff072183e927..3cadc935e67c 100644
--- a/rs/java/android/renderscript/Program.java
+++ b/rs/java/android/renderscript/Program.java
@@ -32,7 +32,11 @@ import java.io.UnsupportedEncodingException;
* Program is a base class for all the objects that modify
* various stages of the graphics pipeline
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Program extends BaseObj {
static final int MAX_INPUT = 8;
static final int MAX_OUTPUT = 8;
diff --git a/rs/java/android/renderscript/ProgramFragment.java b/rs/java/android/renderscript/ProgramFragment.java
index 880531207b4d..e2879d8a0d54 100644
--- a/rs/java/android/renderscript/ProgramFragment.java
+++ b/rs/java/android/renderscript/ProgramFragment.java
@@ -37,6 +37,7 @@ import android.compat.annotation.UnsupportedAppUsage;
* </p>
*
**/
+@Deprecated
public class ProgramFragment extends Program {
ProgramFragment(long id, RenderScript rs) {
super(id, rs);
diff --git a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
index c741ce6e77ed..8dbf6f44f137 100644
--- a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -29,6 +29,7 @@ import android.compat.annotation.UnsupportedAppUsage;
* blended with results of up to two texture lookups.</p
*
**/
+@Deprecated
public class ProgramFragmentFixedFunction extends ProgramFragment {
ProgramFragmentFixedFunction(long id, RenderScript rs) {
super(id, rs);
diff --git a/rs/java/android/renderscript/ProgramRaster.java b/rs/java/android/renderscript/ProgramRaster.java
index a21696c82161..8b53828918a8 100644
--- a/rs/java/android/renderscript/ProgramRaster.java
+++ b/rs/java/android/renderscript/ProgramRaster.java
@@ -25,6 +25,7 @@ import android.compat.annotation.UnsupportedAppUsage;
* Program raster is primarily used to specify whether point sprites are enabled and to control
* the culling mode. By default, back faces are culled.
**/
+@Deprecated
public class ProgramRaster extends BaseObj {
/**
diff --git a/rs/java/android/renderscript/ProgramStore.java b/rs/java/android/renderscript/ProgramStore.java
index 1952b8860033..c94d2534dad6 100644
--- a/rs/java/android/renderscript/ProgramStore.java
+++ b/rs/java/android/renderscript/ProgramStore.java
@@ -34,7 +34,11 @@ import android.os.Build;
* framebuffer</li>
* </ul>
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class ProgramStore extends BaseObj {
/**
* Specifies the function used to determine whether a fragment
diff --git a/rs/java/android/renderscript/ProgramVertex.java b/rs/java/android/renderscript/ProgramVertex.java
index 9257234de42c..ecd8a31ed130 100644
--- a/rs/java/android/renderscript/ProgramVertex.java
+++ b/rs/java/android/renderscript/ProgramVertex.java
@@ -34,7 +34,6 @@
* The signatures don't have to be exact or in any strict order. As long as the input name in the shader
* matches a channel name and size available on the mesh, the runtime takes care of connecting the
* two. Unlike OpenGL, there is no need to link the vertex and fragment programs.</p>
- *
**/
package android.renderscript;
@@ -49,6 +48,7 @@ import android.compat.annotation.UnsupportedAppUsage;
* geometric data in a user-defined way.
*
**/
+@Deprecated
public class ProgramVertex extends Program {
ProgramVertex(long id, RenderScript rs) {
diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
index 03c2eaf91242..4cf2f4c8174f 100644
--- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage;
* without writing any GLSL code.
*
**/
+@Deprecated
public class ProgramVertexFixedFunction extends ProgramVertex {
ProgramVertexFixedFunction(long id, RenderScript rs) {
diff --git a/rs/java/android/renderscript/RSDriverException.java b/rs/java/android/renderscript/RSDriverException.java
index 9e6507f517ed..3d0f0bfadbd0 100644
--- a/rs/java/android/renderscript/RSDriverException.java
+++ b/rs/java/android/renderscript/RSDriverException.java
@@ -20,7 +20,12 @@ package android.renderscript;
/**
* Base class for all exceptions thrown by the Android
* RenderScript
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class RSDriverException extends RSRuntimeException {
public RSDriverException(String string) {
super(string);
diff --git a/rs/java/android/renderscript/RSIllegalArgumentException.java b/rs/java/android/renderscript/RSIllegalArgumentException.java
index 5c68594f33a2..d0ac5b681f2f 100644
--- a/rs/java/android/renderscript/RSIllegalArgumentException.java
+++ b/rs/java/android/renderscript/RSIllegalArgumentException.java
@@ -20,7 +20,12 @@ package android.renderscript;
/**
* Base class for all exceptions thrown by the Android
* RenderScript
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class RSIllegalArgumentException extends RSRuntimeException {
public RSIllegalArgumentException(String string) {
super(string);
diff --git a/rs/java/android/renderscript/RSInvalidStateException.java b/rs/java/android/renderscript/RSInvalidStateException.java
index c881898dab3d..5eea41997f35 100644
--- a/rs/java/android/renderscript/RSInvalidStateException.java
+++ b/rs/java/android/renderscript/RSInvalidStateException.java
@@ -20,7 +20,12 @@ package android.renderscript;
/**
* Base class for all exceptions thrown by the Android
* RenderScript
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class RSInvalidStateException extends RSRuntimeException {
public RSInvalidStateException(String string) {
super(string);
diff --git a/rs/java/android/renderscript/RSRuntimeException.java b/rs/java/android/renderscript/RSRuntimeException.java
index b4b629e14184..d52a1c10add0 100644
--- a/rs/java/android/renderscript/RSRuntimeException.java
+++ b/rs/java/android/renderscript/RSRuntimeException.java
@@ -20,7 +20,12 @@ package android.renderscript;
/**
* Base class for all exceptions thrown by the Android
* RenderScript
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class RSRuntimeException
extends java.lang.RuntimeException {
public RSRuntimeException(String string) {
diff --git a/rs/java/android/renderscript/RSSurfaceView.java b/rs/java/android/renderscript/RSSurfaceView.java
index 6bdde387b334..05c0112f1bb7 100644
--- a/rs/java/android/renderscript/RSSurfaceView.java
+++ b/rs/java/android/renderscript/RSSurfaceView.java
@@ -33,6 +33,7 @@ import android.view.SurfaceView;
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
*/
+@Deprecated
public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder;
private RenderScriptGL mRS;
diff --git a/rs/java/android/renderscript/RSTextureView.java b/rs/java/android/renderscript/RSTextureView.java
index af3258a7090d..ed68fc39ddce 100644
--- a/rs/java/android/renderscript/RSTextureView.java
+++ b/rs/java/android/renderscript/RSTextureView.java
@@ -28,6 +28,7 @@ import android.view.TextureView;
* to draw on.
*
*/
+@Deprecated
public class RSTextureView extends TextureView implements TextureView.SurfaceTextureListener {
private RenderScriptGL mRS;
private SurfaceTexture mSurfaceTexture;
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 806a25a748e2..855cfdcbdf7b 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -44,7 +44,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* <p>For more information about creating an application that uses RenderScript, read the
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class RenderScript {
static final long TRACE_TAG = Trace.TRACE_TAG_RS;
diff --git a/rs/java/android/renderscript/RenderScriptCacheDir.java b/rs/java/android/renderscript/RenderScriptCacheDir.java
index 862d032d6987..cd6e8b14a793 100644
--- a/rs/java/android/renderscript/RenderScriptCacheDir.java
+++ b/rs/java/android/renderscript/RenderScriptCacheDir.java
@@ -23,7 +23,11 @@ import java.io.File;
/**
* Used only for tracking the RenderScript cache directory.
* @hide
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class RenderScriptCacheDir {
/**
* Sets the directory to use as a persistent storage for the
diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java
index dafaf367364d..d46dbf68291b 100644
--- a/rs/java/android/renderscript/RenderScriptGL.java
+++ b/rs/java/android/renderscript/RenderScriptGL.java
@@ -37,6 +37,7 @@ import android.view.SurfaceHolder;
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
**/
+@Deprecated
public class RenderScriptGL extends RenderScript {
int mWidth;
int mHeight;
diff --git a/rs/java/android/renderscript/Sampler.java b/rs/java/android/renderscript/Sampler.java
index 70e88bc51f79..06f036db3aa5 100644
--- a/rs/java/android/renderscript/Sampler.java
+++ b/rs/java/android/renderscript/Sampler.java
@@ -25,7 +25,12 @@ package android.renderscript;
* android.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE}; using a Sampler on
* an {@link android.renderscript.Allocation} that was not created with {@link
* android.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE} is undefined.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Sampler extends BaseObj {
public enum Value {
NEAREST (0),
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index d1d3a7642382..f32a2f7ef482 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -22,7 +22,12 @@ import android.util.SparseArray;
/**
* The parent class for all executable scripts. This should not be used by
* applications.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Script extends BaseObj {
/**
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 00ebe5756589..1866a9983495 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -25,7 +25,12 @@ import java.io.InputStream;
/**
* The superclass for all user-defined scripts. This is only
* intended to be used by the generated derived classes.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class ScriptC extends Script {
private static final String TAG = "ScriptC";
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index e0bdbfcdfed5..5cdb9cf3b8be 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -37,7 +37,12 @@ import java.util.Map;
* Grouping kernels together allows for more efficient execution. For example,
* runtime and compiler optimization can be applied to reduce computation and
* communication overhead, and to make better use of the CPU and the GPU.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptGroup extends BaseObj {
private static final String TAG = "ScriptGroup";
IO mOutputs[];
diff --git a/rs/java/android/renderscript/ScriptIntrinsic.java b/rs/java/android/renderscript/ScriptIntrinsic.java
index 61211a25f8af..8d654221b4a8 100644
--- a/rs/java/android/renderscript/ScriptIntrinsic.java
+++ b/rs/java/android/renderscript/ScriptIntrinsic.java
@@ -23,7 +23,12 @@ package android.renderscript;
* operations.
*
* Not intended for direct use.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public abstract class ScriptIntrinsic extends Script {
ScriptIntrinsic(long id, RenderScript rs) {
super(id, rs);
diff --git a/rs/java/android/renderscript/ScriptIntrinsic3DLUT.java b/rs/java/android/renderscript/ScriptIntrinsic3DLUT.java
index ce149d9a103a..7a2847e3bfcc 100644
--- a/rs/java/android/renderscript/ScriptIntrinsic3DLUT.java
+++ b/rs/java/android/renderscript/ScriptIntrinsic3DLUT.java
@@ -23,7 +23,11 @@ package android.renderscript;
* allocation. The 8 nearest values are sampled and linearly interpolated. The
* result is placed in the output.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsic3DLUT extends ScriptIntrinsic {
private Allocation mLUT;
private Element mElement;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
index 49a71b430ac6..16cc79930b76 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
@@ -29,7 +29,11 @@ import java.lang.annotation.RetentionPolicy;
*
* For detailed description of BLAS, please refer to http://www.netlib.org/blas/
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicBLAS extends ScriptIntrinsic {
private Allocation mLUT;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlend.java b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
index fdcd61b04eca..a1c79ef938c4 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlend.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Intrinsic kernels for blending two {@link android.renderscript.Allocation} objects.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class ScriptIntrinsicBlend extends ScriptIntrinsic {
ScriptIntrinsicBlend(long id, RenderScript rs) {
super(id, rs);
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlur.java b/rs/java/android/renderscript/ScriptIntrinsicBlur.java
index 0891d5142022..68cbc3f3eaad 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlur.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlur.java
@@ -20,8 +20,11 @@ package android.renderscript;
* Intrinsic Gausian blur filter. Applies a gaussian blur of the
* specified radius to all elements of an allocation.
*
- *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicBlur extends ScriptIntrinsic {
private final float[] mValues = new float[9];
private Allocation mInput;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/rs/java/android/renderscript/ScriptIntrinsicColorMatrix.java
index e8a299c28c51..4a05cf54e13f 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicColorMatrix.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicColorMatrix.java
@@ -36,7 +36,12 @@ package android.renderscript;
* Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
* {@link Element#F32}, {@link Element#F32_2}, {@link
* Element#F32_3}, and {@link Element#F32_4}.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
private final Matrix4f mMatrix = new Matrix4f();
private final Float4 mAdd = new Float4();
diff --git a/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
index 9fe7b2d8f0ef..4b9dff18f62e 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -19,7 +19,11 @@ package android.renderscript;
/**
* Intrinsic for applying a 3x3 convolve to an allocation.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
private final float[] mValues = new float[9];
private Allocation mInput;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java b/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
index 8518bb27379d..ed93c7eda0a8 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -19,7 +19,11 @@ package android.renderscript;
/**
* Intrinsic for applying a 5x5 convolve to an allocation.
*
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicConvolve5x5 extends ScriptIntrinsic {
private final float[] mValues = new float[25];
private Allocation mInput;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicHistogram.java b/rs/java/android/renderscript/ScriptIntrinsicHistogram.java
index 0e8b36c11952..4a71bc8c41ca 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicHistogram.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicHistogram.java
@@ -19,8 +19,11 @@ package android.renderscript;
/**
* Intrinsic Histogram filter.
*
- *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicHistogram extends ScriptIntrinsic {
private Allocation mOut;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicLUT.java b/rs/java/android/renderscript/ScriptIntrinsicLUT.java
index e90462d11124..7d5b09fb41aa 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicLUT.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicLUT.java
@@ -21,7 +21,12 @@ package android.renderscript;
* channel of the input has an independant lookup table. The
* tables are 256 entries in size and can cover the full value
* range of {@link Element#U8_4}.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public final class ScriptIntrinsicLUT extends ScriptIntrinsic {
private final Matrix4f mMatrix = new Matrix4f();
private Allocation mTables;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicResize.java b/rs/java/android/renderscript/ScriptIntrinsicResize.java
index 45b0a646b924..a87fe95e2225 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicResize.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicResize.java
@@ -18,7 +18,12 @@ package android.renderscript;
/**
* Intrinsic for performing a resize of a 2D allocation.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public final class ScriptIntrinsicResize extends ScriptIntrinsic {
private Allocation mInput;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicYuvToRGB.java b/rs/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
index e64c91103c8f..a94f9167d953 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -23,7 +23,12 @@ package android.renderscript;
* The input allocation should be supplied in a supported YUV format
* as a YUV element Allocation. The output is RGBA; the alpha channel
* will be set to 255.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public final class ScriptIntrinsicYuvToRGB extends ScriptIntrinsic {
private Allocation mInput;
diff --git a/rs/java/android/renderscript/Short2.java b/rs/java/android/renderscript/Short2.java
index 24809f739159..4565eb4c11d4 100644
--- a/rs/java/android/renderscript/Short2.java
+++ b/rs/java/android/renderscript/Short2.java
@@ -22,7 +22,12 @@ package android.renderscript;
*
* Vector version of the basic short type.
* Provides two short fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Short2 {
public short x;
public short y;
diff --git a/rs/java/android/renderscript/Short3.java b/rs/java/android/renderscript/Short3.java
index 661db0a89f3d..3d70f078e483 100644
--- a/rs/java/android/renderscript/Short3.java
+++ b/rs/java/android/renderscript/Short3.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic short type.
* Provides three short fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Short3 {
public short x;
public short y;
diff --git a/rs/java/android/renderscript/Short4.java b/rs/java/android/renderscript/Short4.java
index a2d74f2ccf94..c90d64876e32 100644
--- a/rs/java/android/renderscript/Short4.java
+++ b/rs/java/android/renderscript/Short4.java
@@ -19,7 +19,12 @@ package android.renderscript;
/**
* Vector version of the basic short type.
* Provides four short fields packed.
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
*/
+@Deprecated
public class Short4 {
public short x;
public short y;
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index dc2378596d00..021fd06b3535 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -42,7 +42,12 @@ package android.renderscript;
* <p>For more information about creating an application that uses RenderScript, read the
* <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
* </div>
+ *
+ * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
+ * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
+ * guide</a> for the proposed alternatives.
**/
+@Deprecated
public class Type extends BaseObj {
int mDimX;
int mDimY;
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index e41073bcdb76..0caba421dca8 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -25,7 +25,7 @@ LOCAL_C_INCLUDES += \
frameworks/rs
LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -Wno-deprecated-declarations
LOCAL_MODULE:= librs_jni
LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b3be0448edaf..e7ffb1a64d4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -309,13 +309,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
public AccessibilityManagerService(Context context) {
mContext = context;
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = context.getSystemService(PowerManager.class);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mA11yController = mWindowManagerService.getAccessibilityController();
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
- mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
+ PolicyWarningUIController policyWarningUIController;
+ if (AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
+ policyWarningUIController = new PolicyWarningUIController(mMainHandler, context,
+ new PolicyWarningUIController.NotificationController(context));
+ }
+ mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
+ this);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
@@ -351,6 +357,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (isA11yTracingEnabled()) {
logTrace(LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
}
+ mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
+ userState.mBoundServices);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
@@ -1302,6 +1310,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
+ mSecurityPolicy.onSwitchUserLocked(mCurrentUserId, userState.mEnabledServices);
// Even if reading did not yield change, we have to update
// the state since the context in which the current user
// state was used has changed since it was inactive.
@@ -3665,6 +3674,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
} else if (mEnabledAccessibilityServicesUri.equals(uri)) {
if (readEnabledAccessibilityServicesLocked(userState)) {
+ mSecurityPolicy.onEnabledServicesChangedLocked(userState.mUserId,
+ userState.mEnabledServices);
userState.updateCrashedServicesIfNeededLocked();
onUserStateChangedLocked(userState);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index bef6d3e950c1..fd355d8da341 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -41,21 +41,19 @@ import com.android.internal.util.ArrayUtils;
import libcore.util.EmptyArray;
+import java.util.ArrayList;
+import java.util.Set;
+
/**
* This class provides APIs of accessibility security policies for accessibility manager
- * to grant accessibility capabilities or events access right to accessibility service.
+ * to grant accessibility capabilities or events access right to accessibility services. And also
+ * monitors the current bound accessibility services to prompt permission warnings for
+ * not accessibility-categorized ones.
*/
public class AccessibilitySecurityPolicy {
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
private static final String LOG_TAG = "AccessibilitySecurityPolicy";
- private final Context mContext;
- private final PackageManager mPackageManager;
- private final UserManager mUserManager;
- private final AppOpsManager mAppOpsManager;
-
- private AppWidgetManagerInternal mAppWidgetService;
-
private static final int KEEP_SOURCE_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED
| AccessibilityEvent.TYPE_VIEW_FOCUSED
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
@@ -72,6 +70,8 @@ public class AccessibilitySecurityPolicy {
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
| AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+ public static final boolean POLICY_WARNING_ENABLED = true;
+
/**
* Methods that should find their way into separate modules, but are still in AMS
* TODO (b/111889696): Refactoring UserState to AccessibilityUserManager.
@@ -84,19 +84,32 @@ public class AccessibilitySecurityPolicy {
// TODO: Should include resolveProfileParentLocked, but that was already in SecurityPolicy
}
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final UserManager mUserManager;
+ private final AppOpsManager mAppOpsManager;
private final AccessibilityUserManager mAccessibilityUserManager;
+ private final PolicyWarningUIController mPolicyWarningUIController;
+ /** All bound accessibility services which don't belong to accessibility category. */
+ private final ArraySet<ComponentName> mNonA11yCategoryServices = new ArraySet<>();
+
+ private AppWidgetManagerInternal mAppWidgetService;
private AccessibilityWindowManager mAccessibilityWindowManager;
+ private int mCurrentUserId = UserHandle.USER_NULL;
/**
* Constructor for AccessibilityManagerService.
*/
- public AccessibilitySecurityPolicy(@NonNull Context context,
+ public AccessibilitySecurityPolicy(PolicyWarningUIController policyWarningUIController,
+ @NonNull Context context,
@NonNull AccessibilityUserManager a11yUserManager) {
mContext = context;
mAccessibilityUserManager = a11yUserManager;
mPackageManager = mContext.getPackageManager();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mPolicyWarningUIController = policyWarningUIController;
+ mPolicyWarningUIController.setAccessibilityPolicyManager(this);
}
/**
@@ -568,4 +581,98 @@ public class AccessibilitySecurityPolicy {
+ permission);
}
}
+
+ /**
+ * Called after a service was bound or unbound. Checks the current bound accessibility
+ * services and updates alarms.
+ *
+ * @param userId The user id
+ * @param boundServices The bound services
+ */
+ public void onBoundServicesChangedLocked(int userId,
+ ArrayList<AccessibilityServiceConnection> boundServices) {
+ if (!POLICY_WARNING_ENABLED) {
+ return;
+ }
+ if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) {
+ return;
+ }
+
+ ArraySet<ComponentName> tempNonA11yCategoryServices = new ArraySet<>();
+ for (int i = 0; i < boundServices.size(); i++) {
+ final AccessibilityServiceInfo a11yServiceInfo = boundServices.get(
+ i).getServiceInfo();
+ final ComponentName service = a11yServiceInfo.getComponentName().clone();
+ if (!isA11yCategoryService(a11yServiceInfo)) {
+ tempNonA11yCategoryServices.add(service);
+ if (mNonA11yCategoryServices.contains(service)) {
+ mNonA11yCategoryServices.remove(service);
+ } else {
+ mPolicyWarningUIController.onNonA11yCategoryServiceBound(userId, service);
+ }
+ }
+ }
+
+ for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
+ final ComponentName service = mNonA11yCategoryServices.valueAt(i);
+ mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(userId, service);
+ }
+ mNonA11yCategoryServices.clear();
+ mNonA11yCategoryServices.addAll(tempNonA11yCategoryServices);
+ }
+
+ /**
+ * Called after switching to another user. Resets data and cancels old alarms after
+ * switching to another user.
+ *
+ * @param userId The user id
+ * @param enabledServices The enabled services
+ */
+ public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) {
+ if (!POLICY_WARNING_ENABLED) {
+ return;
+ }
+ if (mCurrentUserId == userId) {
+ return;
+ }
+
+ mPolicyWarningUIController.onSwitchUserLocked(userId, enabledServices);
+
+ for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
+ mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(mCurrentUserId,
+ mNonA11yCategoryServices.valueAt(i));
+ }
+ mNonA11yCategoryServices.clear();
+ mCurrentUserId = userId;
+ }
+
+ /**
+ * Called after the enabled accessibility services changed.
+ *
+ * @param userId The user id
+ * @param enabledServices The enabled services
+ */
+ public void onEnabledServicesChangedLocked(int userId,
+ Set<ComponentName> enabledServices) {
+ if (!POLICY_WARNING_ENABLED) {
+ return;
+ }
+ if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) {
+ return;
+ }
+
+ mPolicyWarningUIController.onEnabledServicesChangedLocked(userId, enabledServices);
+ }
+
+ /**
+ * Identifies whether the accessibility service is true and designed for accessibility. An
+ * accessibility service is considered as accessibility category if
+ * {@link AccessibilityServiceInfo#isAccessibilityTool} is true.
+ *
+ * @param serviceInfo The accessibility service's serviceInfo.
+ * @return Returns true if it is a true accessibility service.
+ */
+ public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
+ return serviceInfo.isAccessibilityTool();
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
new file mode 100644
index 000000000000..ea3e650a564a
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static android.app.AlarmManager.RTC_WAKEUP;
+
+import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_VIEW_AND_CONTROL_ACCESS;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.Manifest;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.app.ActivityOptions;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.ImageUtils;
+
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The class handles permission warning notifications for not accessibility-categorized
+ * accessibility services from {@link AccessibilitySecurityPolicy}. And also maintains the setting
+ * {@link Settings.Secure#NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES} in order not to
+ * resend notifications to the same service.
+ */
+public class PolicyWarningUIController {
+ private static final String TAG = PolicyWarningUIController.class.getSimpleName();
+ @VisibleForTesting
+ protected static final String ACTION_SEND_NOTIFICATION = TAG + ".ACTION_SEND_NOTIFICATION";
+ @VisibleForTesting
+ protected static final String ACTION_A11Y_SETTINGS = TAG + ".ACTION_A11Y_SETTINGS";
+ @VisibleForTesting
+ protected static final String ACTION_DISMISS_NOTIFICATION =
+ TAG + ".ACTION_DISMISS_NOTIFICATION";
+ private static final int SEND_NOTIFICATION_DELAY_HOURS = 24;
+
+ /** Current enabled accessibility services. */
+ private final ArraySet<ComponentName> mEnabledA11yServices = new ArraySet<>();
+
+ private final Handler mMainHandler;
+ private final AlarmManager mAlarmManager;
+ private final Context mContext;
+ private final NotificationController mNotificationController;
+
+ public PolicyWarningUIController(@NonNull Handler handler, @NonNull Context context,
+ NotificationController notificationController) {
+ mMainHandler = handler;
+ mContext = context;
+ mNotificationController = notificationController;
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SEND_NOTIFICATION);
+ filter.addAction(ACTION_A11Y_SETTINGS);
+ filter.addAction(ACTION_DISMISS_NOTIFICATION);
+ mContext.registerReceiver(mNotificationController, filter,
+ Manifest.permission.MANAGE_ACCESSIBILITY, mMainHandler);
+
+ }
+
+ protected void setAccessibilityPolicyManager(
+ AccessibilitySecurityPolicy accessibilitySecurityPolicy) {
+ mNotificationController.setAccessibilityPolicyManager(accessibilitySecurityPolicy);
+ }
+
+ /**
+ * Updates enabled accessibility services and notified accessibility services after switching
+ * to another user.
+ *
+ * @param enabledServices The current enabled services
+ */
+ public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) {
+ mEnabledA11yServices.clear();
+ mEnabledA11yServices.addAll(enabledServices);
+ mMainHandler.sendMessage(obtainMessage(mNotificationController::onSwitchUser, userId));
+ }
+
+ /**
+ * Computes the newly disabled services and removes its record from the setting
+ * {@link Settings.Secure#NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES} after detecting the
+ * setting {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} changed.
+ *
+ * @param userId The user id
+ * @param enabledServices The enabled services
+ */
+ public void onEnabledServicesChangedLocked(int userId,
+ Set<ComponentName> enabledServices) {
+ final ArraySet<ComponentName> disabledServices = new ArraySet<>(mEnabledA11yServices);
+ disabledServices.removeAll(enabledServices);
+ mEnabledA11yServices.clear();
+ mEnabledA11yServices.addAll(enabledServices);
+ mMainHandler.sendMessage(
+ obtainMessage(mNotificationController::onServicesDisabled, userId,
+ disabledServices));
+ }
+
+ /**
+ * Called when the target service is bound. Sets an 24 hours alarm to the service which is not
+ * notified yet to execute action {@code ACTION_SEND_NOTIFICATION}.
+ *
+ * @param userId The user id
+ * @param service The service's component name
+ */
+ public void onNonA11yCategoryServiceBound(int userId, ComponentName service) {
+ mMainHandler.sendMessage(obtainMessage(this::setAlarm, userId, service));
+ }
+
+ /**
+ * Called when the target service is unbound. Cancels the old alarm with intent action
+ * {@code ACTION_SEND_NOTIFICATION} from the service.
+ *
+ * @param userId The user id
+ * @param service The service's component name
+ */
+ public void onNonA11yCategoryServiceUnbound(int userId, ComponentName service) {
+ mMainHandler.sendMessage(obtainMessage(this::cancelAlarm, userId, service));
+ }
+
+ private void setAlarm(int userId, ComponentName service) {
+ final Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.HOUR, SEND_NOTIFICATION_DELAY_HOURS);
+ mAlarmManager.set(RTC_WAKEUP, cal.getTimeInMillis(),
+ createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION,
+ service.flattenToShortString()));
+ }
+
+ private void cancelAlarm(int userId, ComponentName service) {
+ mAlarmManager.cancel(
+ createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION,
+ service.flattenToShortString()));
+ }
+
+ protected static PendingIntent createPendingIntent(Context context, int userId, String action,
+ String serviceComponentName) {
+ final Intent intent = new Intent(action);
+ intent.setPackage(context.getPackageName())
+ .setIdentifier(serviceComponentName)
+ .putExtra(Intent.EXTRA_USER_ID, userId);
+ return PendingIntent.getBroadcast(context, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ /** A sub class to handle notifications and settings on the main thread. */
+ @MainThread
+ public static class NotificationController extends BroadcastReceiver {
+ private static final char RECORD_SEPARATOR = ':';
+
+ /** All accessibility services which are notified to the user by the policy warning rule. */
+ private final ArraySet<ComponentName> mNotifiedA11yServices = new ArraySet<>();
+ private final NotificationManager mNotificationManager;
+ private final Context mContext;
+
+ private int mCurrentUserId;
+ private AccessibilitySecurityPolicy mAccessibilitySecurityPolicy;
+
+ public NotificationController(Context context) {
+ mContext = context;
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+ }
+
+ protected void setAccessibilityPolicyManager(
+ AccessibilitySecurityPolicy accessibilitySecurityPolicy) {
+ mAccessibilitySecurityPolicy = accessibilitySecurityPolicy;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final String service = intent.getIdentifier();
+ final ComponentName componentName = ComponentName.unflattenFromString(service);
+ if (TextUtils.isEmpty(action) || TextUtils.isEmpty(service)
+ || componentName == null) {
+ return;
+ }
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM);
+ if (ACTION_SEND_NOTIFICATION.equals(action)) {
+ trySendNotification(userId, componentName);
+ } else if (ACTION_A11Y_SETTINGS.equals(action)) {
+ launchSettings(userId, componentName);
+ mNotificationManager.cancel(service, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
+ onNotificationCanceled(userId, componentName);
+ } else if (ACTION_DISMISS_NOTIFICATION.equals(action)) {
+ onNotificationCanceled(userId, componentName);
+ }
+ }
+
+ protected void onSwitchUser(int userId) {
+ mCurrentUserId = userId;
+ mNotifiedA11yServices.clear();
+ mNotifiedA11yServices.addAll(readNotifiedServiceList(userId));
+ }
+
+ protected void onServicesDisabled(int userId,
+ ArraySet<ComponentName> disabledServices) {
+ if (mNotifiedA11yServices.removeAll(disabledServices)) {
+ writeNotifiedServiceList(userId, mNotifiedA11yServices);
+ }
+ }
+
+ private void trySendNotification(int userId, ComponentName componentName) {
+ if (!AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
+ return;
+ }
+ if (userId != mCurrentUserId) {
+ return;
+ }
+
+ List<AccessibilityServiceInfo> enabledServiceInfos = getEnabledServiceInfos();
+ for (int i = 0; i < enabledServiceInfos.size(); i++) {
+ final AccessibilityServiceInfo a11yServiceInfo = enabledServiceInfos.get(i);
+ if (componentName.flattenToShortString().equals(
+ a11yServiceInfo.getComponentName().flattenToShortString())) {
+ if (!mAccessibilitySecurityPolicy.isA11yCategoryService(a11yServiceInfo)
+ && !mNotifiedA11yServices.contains(componentName)) {
+ final CharSequence displayName =
+ a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(
+ mContext.getPackageManager());
+ final Drawable drawable = a11yServiceInfo.getResolveInfo().loadIcon(
+ mContext.getPackageManager());
+ final int size = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.app_icon_size);
+ sendNotification(userId, componentName.flattenToShortString(),
+ displayName,
+ ImageUtils.buildScaledBitmap(drawable, size, size));
+ }
+ break;
+ }
+ }
+ }
+
+ private void launchSettings(int userId, ComponentName componentName) {
+ if (userId != mCurrentUserId) {
+ return;
+ }
+ final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName.flattenToShortString());
+ final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(
+ mContext.getDisplayId()).toBundle();
+ mContext.startActivityAsUser(intent, bundle, UserHandle.of(userId));
+ mContext.getSystemService(StatusBarManager.class).collapsePanels();
+ }
+
+ protected void onNotificationCanceled(int userId, ComponentName componentName) {
+ if (userId != mCurrentUserId) {
+ return;
+ }
+
+ if (mNotifiedA11yServices.add(componentName)) {
+ writeNotifiedServiceList(userId, mNotifiedA11yServices);
+ }
+ }
+
+ private void sendNotification(int userId, String serviceComponentName, CharSequence name,
+ Bitmap bitmap) {
+ final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
+ SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
+ notificationBuilder.setSmallIcon(R.drawable.ic_accessibility_24dp)
+ .setContentTitle(
+ mContext.getString(R.string.view_and_control_notification_title))
+ .setContentText(
+ mContext.getString(R.string.view_and_control_notification_content,
+ name))
+ .setStyle(new Notification.BigTextStyle()
+ .bigText(
+ mContext.getString(
+ R.string.view_and_control_notification_content,
+ name)))
+ .setTicker(mContext.getString(R.string.view_and_control_notification_title))
+ .setOnlyAlertOnce(true)
+ .setDeleteIntent(
+ createPendingIntent(mContext, userId, ACTION_DISMISS_NOTIFICATION,
+ serviceComponentName))
+ .setContentIntent(
+ createPendingIntent(mContext, userId, ACTION_A11Y_SETTINGS,
+ serviceComponentName));
+ if (bitmap != null) {
+ notificationBuilder.setLargeIcon(bitmap);
+ }
+ mNotificationManager.notify(serviceComponentName, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS,
+ notificationBuilder.build());
+ }
+
+ private ArraySet<ComponentName> readNotifiedServiceList(int userId) {
+ final String notifiedServiceSetting = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
+ userId);
+ if (TextUtils.isEmpty(notifiedServiceSetting)) {
+ return new ArraySet<>();
+ }
+
+ final TextUtils.StringSplitter componentNameSplitter =
+ new TextUtils.SimpleStringSplitter(RECORD_SEPARATOR);
+ componentNameSplitter.setString(notifiedServiceSetting);
+
+ final ArraySet<ComponentName> notifiedServices = new ArraySet<>();
+ final Iterator<String> it = componentNameSplitter.iterator();
+ while (it.hasNext()) {
+ final String componentNameString = it.next();
+ final ComponentName notifiedService = ComponentName.unflattenFromString(
+ componentNameString);
+ if (notifiedService != null) {
+ notifiedServices.add(notifiedService);
+ }
+ }
+ return notifiedServices;
+ }
+
+ private void writeNotifiedServiceList(int userId, ArraySet<ComponentName> services) {
+ StringBuilder notifiedServicesBuilder = new StringBuilder();
+ for (int i = 0; i < services.size(); i++) {
+ if (i > 0) {
+ notifiedServicesBuilder.append(RECORD_SEPARATOR);
+ }
+ final ComponentName notifiedService = services.valueAt(i);
+ notifiedServicesBuilder.append(notifiedService.flattenToShortString());
+ }
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
+ notifiedServicesBuilder.toString(), userId);
+ }
+
+ @VisibleForTesting
+ protected List<AccessibilityServiceInfo> getEnabledServiceInfos() {
+ final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
+ mContext);
+ return accessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index c7f0efa18568..3d07da5fbb30 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -118,6 +118,7 @@ public class FullScreenMagnificationController {
private static final int INVALID_ID = -1;
private int mIdOfLastServiceToMagnify = INVALID_ID;
+ private boolean mMagnificationActivated = false;
DisplayMagnification(int displayId) {
mDisplayId = displayId;
@@ -322,6 +323,13 @@ public class FullScreenMagnificationController {
mSpecAnimationBridge, spec, animationCallback);
mControllerCtx.getHandler().sendMessage(m);
}
+
+ final boolean lastMagnificationActivated = mMagnificationActivated;
+ mMagnificationActivated = spec.scale > 1.0f;
+ if (mMagnificationActivated != lastMagnificationActivated) {
+ mMagnificationRequestObserver.onFullScreenMagnificationActivationState(
+ mMagnificationActivated);
+ }
}
/**
@@ -1506,5 +1514,14 @@ public class FullScreenMagnificationController {
* @param serviceId the ID of the service requesting the change
*/
void onRequestMagnificationSpec(int displayId, int serviceId);
+
+ /**
+ * Called when the state of the magnification activation is changed.
+ * It is for the logging data of the magnification activation state.
+ *
+ * @param activated {@code true} if the magnification is activated, otherwise {@code false}.
+ */
+ @GuardedBy("mLock")
+ void onFullScreenMagnificationActivationState(boolean activated);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 2a65b6423158..17a7d393f369 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility.magnification;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
@@ -25,11 +26,13 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -51,7 +54,8 @@ import com.android.server.accessibility.AccessibilityManagerService;
* </ol>
*/
public class MagnificationController implements WindowMagnificationManager.Callback,
- MagnificationGestureHandler.Callback {
+ MagnificationGestureHandler.Callback,
+ FullScreenMagnificationController.MagnificationRequestObserver {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
@@ -66,6 +70,9 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private WindowMagnificationManager mWindowMagnificationMgr;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ private long mWindowModeEnabledTime = 0;
+ private long mFullScreenModeEnabledTime = 0;
+
/**
* A callback to inform the magnification transition result.
*/
@@ -187,7 +194,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
}
- void onRequestMagnificationSpec(int displayId, int serviceId) {
+ @Override
+ public void onRequestMagnificationSpec(int displayId, int serviceId) {
synchronized (mLock) {
if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) {
return;
@@ -200,6 +208,39 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
}
+ // TODO : supporting multi-display (b/182227245).
+ @Override
+ public void onWindowMagnificationActivationState(boolean activated) {
+ if (activated) {
+ mWindowModeEnabledTime = SystemClock.uptimeMillis();
+ } else {
+ logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ SystemClock.uptimeMillis() - mWindowModeEnabledTime);
+ }
+ }
+
+ @Override
+ public void onFullScreenMagnificationActivationState(boolean activated) {
+ if (activated) {
+ mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
+ } else {
+ logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
+ SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
+ }
+ }
+
+ /**
+ * Wrapper method of logging the magnification activated mode and its duration of the usage
+ * when the magnification is disabled.
+ *
+ * @param mode The activated magnification mode.
+ * @param duration The duration in milliseconds during the magnification is activated.
+ */
+ @VisibleForTesting
+ public void logMagnificationUsageState(int mode, long duration) {
+ AccessibilityStatsLogUtils.logMagnificationUsageState(mode, duration);
+ }
+
/**
* Updates the active user ID of {@link FullScreenMagnificationController} and {@link
* WindowMagnificationManager}.
@@ -260,7 +301,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
if (mFullScreenMagnificationController == null) {
mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
- mAms, mLock, this::onRequestMagnificationSpec);
+ mAms, mLock, this);
mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
}
}
@@ -340,7 +381,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mTransitionCallBack = transitionCallBack;
mDisplayId = displayId;
mTargetMode = targetMode;
- mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ mCurrentMode = mTargetMode ^ ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
mCurrentScale = scale;
mCurrentCenter.set(currentCenter);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 40668d8df568..ded601e70ff1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -93,6 +93,13 @@ public class WindowMagnificationManager implements
* @param scale the target scale, or {@link Float#NaN} to leave unchanged
*/
void onPerformScaleAction(int displayId, float scale);
+
+ /**
+ * Called when the state of the magnification activation is changed.
+ *
+ * @param activated {@code true} if the magnification is activated, otherwise {@code false}.
+ */
+ void onWindowMagnificationActivationState(boolean activated);
}
private final Callback mCallback;
@@ -264,6 +271,7 @@ public class WindowMagnificationManager implements
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback) {
+ final boolean enabled;
synchronized (mLock) {
if (mConnectionWrapper == null) {
return;
@@ -272,9 +280,13 @@ public class WindowMagnificationManager implements
if (magnifier == null) {
magnifier = createWindowMagnifier(displayId);
}
- magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
+ enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
animationCallback);
}
+
+ if (enabled) {
+ mCallback.onWindowMagnificationActivationState(true);
+ }
}
/**
@@ -296,16 +308,21 @@ public class WindowMagnificationManager implements
*/
void disableWindowMagnification(int displayId, boolean clear,
MagnificationAnimationCallback animationCallback) {
+ final boolean disabled;
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null || mConnectionWrapper == null) {
return;
}
- magnifier.disableWindowMagnificationInternal(animationCallback);
+ disabled = magnifier.disableWindowMagnificationInternal(animationCallback);
if (clear) {
mWindowMagnifiers.delete(displayId);
}
}
+
+ if (disabled) {
+ mCallback.onWindowMagnificationActivationState(false);
+ }
}
/**
@@ -560,26 +577,35 @@ public class WindowMagnificationManager implements
}
@GuardedBy("mLock")
- void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
+ boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback) {
if (mEnabled) {
- return;
+ return false;
}
final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
centerX, centerY, animationCallback)) {
mScale = normScale;
mEnabled = true;
+
+ return true;
}
+ return false;
}
@GuardedBy("mLock")
- void disableWindowMagnificationInternal(
+ boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
- if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
+ if (!mEnabled) {
+ return false;
+ }
+ if (mWindowMagnificationManager.disableWindowMagnificationInternal(
mDisplayId, animationResultCallback)) {
mEnabled = false;
+
+ return true;
}
+ return false;
}
@GuardedBy("mLock")
diff --git a/services/api/Android.bp b/services/api/Android.bp
index e69de29bb2d1..b8ca5488c5cd 100644
--- a/services/api/Android.bp
+++ b/services/api/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "non-updatable-system-server-current.txt",
+ srcs: ["non-updatable-current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+ name: "non-updatable-system-server-removed.txt",
+ srcs: ["non-updatable-removed.txt"],
+ visibility: ["//frameworks/base/api"],
+} \ No newline at end of file
diff --git a/services/api/current.txt b/services/api/current.txt
index 7e8f7a20bd64..a3e671520753 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -88,6 +88,14 @@ package com.android.server {
}
+package com.android.server.am {
+
+ public interface ActivityManagerLocal {
+ method public boolean canStartForegroundService(int, int, @NonNull String);
+ }
+
+}
+
package com.android.server.role {
public interface RoleServicePlatformHelper {
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
index 3c72d38927bc..f01c1824116c 100644
--- a/services/api/non-updatable-current.txt
+++ b/services/api/non-updatable-current.txt
@@ -35,6 +35,14 @@ package com.android.server {
}
+package com.android.server.am {
+
+ public interface ActivityManagerLocal {
+ method public boolean canStartForegroundService(int, int, @NonNull String);
+ }
+
+}
+
package com.android.server.role {
public interface RoleServicePlatformHelper {
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index b15d07b0f1a9..9d4c9ebbf592 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -16,10 +16,13 @@
package com.android.server.autofill.ui;
+import static android.view.inputmethod.InlineSuggestionInfo.TYPE_SUGGESTION;
+
import static com.android.server.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
@@ -49,6 +52,9 @@ final class InlineSuggestionFactory {
InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId,
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication),
+ createInlineSuggestionTooltip(inlineFillUiInfo.mInlineRequest,
+ inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
+ response.getInlineTooltipPresentation()),
uiCallback);
}
@@ -66,6 +72,8 @@ final class InlineSuggestionFactory {
final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest;
SparseArray<Pair<Dataset, InlineSuggestion>> response = new SparseArray<>(datasets.size());
+
+ boolean hasTooltip = false;
for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
final Dataset dataset = datasets.get(datasetIndex);
final int fieldIndex = dataset.getFieldIds().indexOf(inlineFillUiInfo.mFocusedId);
@@ -82,14 +90,25 @@ final class InlineSuggestionFactory {
}
final String suggestionType =
- dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION
+ dataset.getAuthentication() == null ? TYPE_SUGGESTION
: InlineSuggestionInfo.TYPE_ACTION;
final int index = datasetIndex;
+ InlineSuggestion inlineSuggestionTooltip = null;
+ if (!hasTooltip) {
+ // Only available for first one inline suggestion tooltip.
+ inlineSuggestionTooltip = createInlineSuggestionTooltip(request,
+ inlineFillUiInfo, suggestionSource,
+ dataset.getFieldInlineTooltipPresentation(fieldIndex));
+ if (inlineSuggestionTooltip != null) {
+ hasTooltip = true;
+ }
+ }
InlineSuggestion inlineSuggestion = createInlineSuggestion(
inlineFillUiInfo, suggestionSource, suggestionType,
() -> uiCallback.autofill(dataset, index),
mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+ inlineSuggestionTooltip,
uiCallback);
response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
}
@@ -103,11 +122,13 @@ final class InlineSuggestionFactory {
@NonNull @InlineSuggestionInfo.Type String suggestionType,
@NonNull Runnable onClickAction,
@NonNull InlinePresentation inlinePresentation,
+ @Nullable InlineSuggestion tooltip,
@NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(), suggestionSource,
inlinePresentation.getAutofillHints(), suggestionType,
- inlinePresentation.isPinned());
+ inlinePresentation.isPinned(), tooltip);
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlineFillUiInfo, inlinePresentation,
@@ -135,6 +156,60 @@ final class InlineSuggestionFactory {
inlinePresentation.isPinned());
}
+ // TODO(182306770): creates new class instead of the InlineSuggestion.
+ private static InlineSuggestion createInlineSuggestionTooltip(
+ @NonNull InlineSuggestionsRequest request,
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
+ String suggestionSource,
+ @NonNull InlinePresentation tooltipPresentation) {
+ if (tooltipPresentation == null) {
+ return null;
+ }
+
+ final InlinePresentationSpec spec = request.getInlineTooltipPresentationSpec();
+ InlinePresentationSpec mergedSpec;
+ if (spec == null) {
+ mergedSpec = tooltipPresentation.getInlinePresentationSpec();
+ } else {
+ mergedSpec = new InlinePresentationSpec.Builder(
+ tooltipPresentation.getInlinePresentationSpec().getMinSize(),
+ tooltipPresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
+ spec.getStyle()).build();
+ }
+
+ InlineFillUi.InlineSuggestionUiCallback uiCallback =
+ new InlineFillUi.InlineSuggestionUiCallback() {
+ @Override
+ public void autofill(Dataset dataset, int datasetIndex) {
+ /* nothing */
+ }
+
+ @Override
+ public void authenticate(int requestId, int datasetIndex) {
+ /* nothing */
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
+ /* nothing */
+ }
+
+ @Override
+ public void onError() {
+ Slog.w(TAG, "An error happened on the tooltip");
+ }
+ };
+
+ InlinePresentation tooltipInline = new InlinePresentation(tooltipPresentation.getSlice(),
+ mergedSpec, false);
+ IInlineContentProvider tooltipContentProvider = createInlineContentProvider(
+ inlineFillUiInfo, tooltipInline, () -> { /* no operation */ }, uiCallback);
+ final InlineSuggestionInfo tooltipInlineSuggestionInfo = new InlineSuggestionInfo(
+ mergedSpec, suggestionSource, /* autofillHints */ null, TYPE_SUGGESTION,
+ /* pinned */ false, /* tooltip */ null);
+ return new InlineSuggestion(tooltipInlineSuggestionInfo, tooltipContentProvider);
+ }
+
private static IInlineContentProvider createInlineContentProvider(
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 52237c934797..92af0804955f 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -766,6 +766,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
.get(association.getPackageName());
if (serviceConnector != null) {
serviceConnector.unbind();
+ restartBleScan();
}
}
}
@@ -962,7 +963,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
- a -> Objects.equals(packageFilter, a.getPackageName()));
+ // Null filter == get all associations
+ a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
}
private Set<Association> getAllAssociations() {
@@ -982,8 +984,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
- a -> Objects.equals(packageFilter, a.getPackageName())
- && Objects.equals(addressFilter, a.getDeviceMacAddress()));
+ // Null filter == get all associations
+ a -> (packageFilter == null || Objects.equals(packageFilter, a.getPackageName()))
+ && (addressFilter == null
+ || Objects.equals(addressFilter, a.getDeviceMacAddress())));
}
private Set<Association> readAllAssociations(int userId) {
@@ -1067,11 +1071,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
void onDeviceDisconnected(String address) {
- Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
+ Slog.d(LOG_TAG, "onDeviceDisconnected(address = " + address + ")");
mCurrentlyConnectedDevices.remove(address);
- onDeviceDisappeared(address);
+ Date lastSeen = mDevicesLastNearby.get(address);
+ if (isDeviceDisappeared(lastSeen)) {
+ onDeviceDisappeared(address);
+ }
+ }
+
+ private boolean isDeviceDisappeared(Date lastSeen) {
+ return lastSeen == null || System.currentTimeMillis() - lastSeen.getTime()
+ >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS;
}
private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
@@ -1172,8 +1184,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
String address = mDevicesLastNearby.keyAt(i);
Date lastNearby = mDevicesLastNearby.valueAt(i);
- if (System.currentTimeMillis() - lastNearby.getTime()
- >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
+ if (isDeviceDisappeared(lastNearby)) {
for (Association association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
getDeviceListenerServiceConnector(association).unbind();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8ccfad6fe061..ed2e62513ab3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -81,10 +81,19 @@ genrule {
out: ["services.core.protolog.json"],
}
+genrule {
+ name: "statslog-art-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module art" +
+ " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource",
+ out: ["com/android/internal/art/ArtStatsLog.java"],
+}
+
java_library_static {
name: "services.core.unboosted",
defaults: ["platform_service_defaults"],
srcs: [
+ ":statslog-art-java-gen",
":services.core-sources",
":services.core.protologsrc",
":dumpstate_aidl",
@@ -98,7 +107,6 @@ java_library_static {
":platform-compat-overrides",
":display-device-config",
":display-layout-config",
- ":cec-config",
":device-state-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
@@ -119,7 +127,6 @@ java_library_static {
],
required: [
- "cec_config.xml",
"gps_debug.conf",
"protolog.conf.json.gz",
],
@@ -185,11 +192,6 @@ java_library_host {
}
prebuilt_etc {
- name: "cec_config.xml",
- src: "java/com/android/server/hdmi/cec_config.xml",
-}
-
-prebuilt_etc {
name: "gps_debug.conf",
src: "java/com/android/server/location/gnss/gps_debug.conf",
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index bb4bbd5bc6d4..1e608f5c1240 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -918,19 +918,7 @@ public final class BatteryService extends SystemService {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
- if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
- }
- mHealthInfo.chargerAcOnline = false;
- mHealthInfo.chargerUsbOnline = false;
- mHealthInfo.chargerWirelessOnline = false;
- final long ident = Binder.clearCallingIdentity();
- try {
- mUpdatesStopped = true;
- processValuesFromShellLocked(pw, opts);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} break;
case "set": {
int opts = parseOptions(shell);
@@ -990,7 +978,8 @@ public final class BatteryService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
- processValuesFromShellLocked(pw, opts);
+ processValuesLocked(
+ /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1004,30 +993,12 @@ public final class BatteryService extends SystemService {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mUpdatesStopped) {
- mUpdatesStopped = false;
- copy(mHealthInfo, mLastHealthInfo);
- processValuesFromShellLocked(pw, opts);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- if (mBatteryInputSuspended) {
- PowerProperties.battery_input_suspended(false);
- mBatteryInputSuspended = false;
- }
+ resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} break;
case "suspend_input": {
- if (!Build.IS_DEBUGGABLE) {
- throw new SecurityException(
- "battery suspend_input is only supported on debuggable builds");
- }
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
- PowerProperties.battery_input_suspended(true);
- mBatteryInputSuspended = true;
+ suspendBatteryInput();
} break;
default:
return shell.handleDefaultCommands(cmd);
@@ -1035,9 +1006,59 @@ public final class BatteryService extends SystemService {
return 0;
}
- private void processValuesFromShellLocked(PrintWriter pw, int opts) {
- processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
- if ((opts & OPTION_FORCE_UPDATE) != 0) {
+ private void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ if (!mUpdatesStopped) {
+ copy(mLastHealthInfo, mHealthInfo);
+ }
+ mHealthInfo.chargerAcOnline = online;
+ mUpdatesStopped = true;
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+ }
+
+ private void setBatteryLevel(int level, boolean forceUpdate) {
+ if (!mUpdatesStopped) {
+ copy(mLastHealthInfo, mHealthInfo);
+ }
+ mHealthInfo.batteryLevel = level;
+ mUpdatesStopped = true;
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+ }
+
+ private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
+ if (!mUpdatesStopped) {
+ copy(mLastHealthInfo, mHealthInfo);
+ }
+ mHealthInfo.chargerAcOnline = false;
+ mHealthInfo.chargerUsbOnline = false;
+ mHealthInfo.chargerWirelessOnline = false;
+ mUpdatesStopped = true;
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+ }
+
+ private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
+ if (mUpdatesStopped) {
+ mUpdatesStopped = false;
+ copy(mHealthInfo, mLastHealthInfo);
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+ }
+ if (mBatteryInputSuspended) {
+ PowerProperties.battery_input_suspended(false);
+ mBatteryInputSuspended = false;
+ }
+ }
+
+ private void suspendBatteryInput() {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException(
+ "battery suspend_input is only supported on debuggable builds");
+ }
+ PowerProperties.battery_input_suspended(true);
+ mBatteryInputSuspended = true;
+ }
+
+ private void processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw) {
+ processValuesLocked(forceUpdate);
+ if (pw != null && forceUpdate) {
pw.println(mSequence);
}
}
@@ -1363,6 +1384,41 @@ public final class BatteryService extends SystemService {
return mInvalidCharger;
}
}
+
+ @Override
+ public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.setChargerAcOnline(online, forceUpdate);
+ }
+
+ @Override
+ public void setBatteryLevel(int level, boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.setBatteryLevel(level, forceUpdate);
+ }
+
+ @Override
+ public void unplugBattery(boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.unplugBattery(forceUpdate, /* printWriter= */ null);
+ }
+
+ @Override
+ public void resetBattery(boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.resetBattery(forceUpdate, /* printWriter= */ null);
+ }
+
+ @Override
+ public void suspendBatteryInput() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.suspendBatteryInput();
+ }
}
/**
@@ -1539,6 +1595,8 @@ public final class BatteryService extends SystemService {
if (Objects.equals(newService, oldService)) return;
Slog.i(TAG, "health: new instance registered " + mInstanceName);
+ // #init() may be called with null callback. Skip null callbacks.
+ if (mCallback == null) return;
mCallback.onRegistration(oldService, newService, mInstanceName);
} catch (NoSuchElementException | RemoteException ex) {
Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
index 2dcf82ff9410..611a37de70f4 100644
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -17,6 +17,9 @@
package com.android.server;
import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import java.util.ArrayList;
/**
* The BluetoothDeviceConfigListener handles system device config change callback and checks
@@ -30,10 +33,12 @@ import android.provider.DeviceConfig;
class BluetoothDeviceConfigListener {
private static final String TAG = "BluetoothDeviceConfigListener";
- BluetoothManagerService mService;
+ private final BluetoothManagerService mService;
+ private final boolean mLogDebug;
- BluetoothDeviceConfigListener(BluetoothManagerService service) {
+ BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
mService = service;
+ mLogDebug = logDebug;
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_BLUETOOTH,
(Runnable r) -> r.run(),
@@ -47,6 +52,13 @@ class BluetoothDeviceConfigListener {
if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
return;
}
+ if (mLogDebug) {
+ ArrayList<String> flags = new ArrayList<>();
+ for (String name : properties.getKeyset()) {
+ flags.add(name + "='" + properties.getString(name, "") + "'");
+ }
+ Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
+ }
boolean foundInit = false;
for (String name : properties.getKeyset()) {
if (name.startsWith("INIT_")) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index dc24ffdb936d..f0c9ba93b080 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -454,6 +454,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
&& state == BluetoothProfile.STATE_DISCONNECTED
&& !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
onInitFlagsChanged();
}
}
@@ -820,6 +821,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return enabledProfiles;
}
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
+ 0) != 0;
+ }
+
+ // Monitor change of BLE scan only mode settings.
+ private void registerForProvisioningStateChange() {
+ ContentObserver contentObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (!isDeviceProvisioned()) {
+ if (DBG) {
+ Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
+ + "provisioned");
+ }
+ return;
+ }
+ if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
+ Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
+ onInitFlagsChanged();
+ }
+ }
+ };
+
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
+ contentObserver);
+ }
+
// Monitor change of BLE scan only mode settings.
private void registerForBleScanModeChange() {
ContentObserver contentObserver = new ContentObserver(null) {
@@ -1385,7 +1415,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mBluetoothAirplaneModeListener != null) {
mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
}
- mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
+ registerForProvisioningStateChange();
+ mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
}
/**
@@ -2229,12 +2260,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+ + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+ + " ms due to existing connections");
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_INIT_FLAGS_CHANGED,
+ DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+ break;
+ }
+ if (!isDeviceProvisioned()) {
+ Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+ + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+ + "ms because device is not provisioned");
mHandler.sendEmptyMessageDelayed(
MESSAGE_INIT_FLAGS_CHANGED,
DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
break;
}
if (mBluetooth != null && isEnabled()) {
+ Slog.i(TAG, "Restarting Bluetooth due to init flag change");
restartForReason(
BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 07b473caa9cc..aa903087af8a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,10 @@
package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
@@ -28,15 +32,30 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
@@ -53,8 +72,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.NetworkPolicyManager.RULE_NONE;
-import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
@@ -85,6 +105,8 @@ import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivitySettingsManager;
import android.net.DataStallReportParcelable;
import android.net.DnsResolverServiceManager;
import android.net.ICaptivePortal;
@@ -95,8 +117,7 @@ import android.net.INetd;
import android.net.INetworkActivityListener;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
-import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
@@ -109,13 +130,14 @@ import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMonitorManager;
import android.net.NetworkPolicyManager;
+import android.net.NetworkPolicyManager.NetworkPolicyCallback;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
+import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
@@ -142,6 +164,7 @@ import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnTransportInfo;
+import android.net.ConnectivityResources;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
@@ -175,6 +198,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.sysprop.NetworkProperties;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -186,22 +210,18 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.connectivity.aidl.INetworkAgent;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.PermissionUtils;
-import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
@@ -214,10 +234,10 @@ import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
+import com.android.server.connectivity.ProfileNetworkPreferences;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.utils.PriorityDump;
import libcore.io.IoUtils;
@@ -311,14 +331,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
private volatile boolean mLockdownEnabled;
/**
- * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
- * handler thread, they don't need a lock.
+ * Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in
+ * internal handler thread, they don't need a lock.
*/
- private SparseIntArray mUidRules = new SparseIntArray();
- /** Flag indicating if background data is restricted. */
- private boolean mRestrictBackground;
+ private SparseIntArray mUidBlockedReasons = new SparseIntArray();
private final Context mContext;
+ private final ConnectivityResources mResources;
// The Context is created for UserHandle.ALL.
private final Context mUserAllContext;
private final Dependencies mDeps;
@@ -346,8 +365,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private String mCurrentTcpBufferSizes;
private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
- new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
- NetworkAgentInfo.class });
+ new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
private enum ReapUnvalidatedNetworks {
// Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -490,16 +508,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Handle private DNS validation status updates.
private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
- /**
- * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
- */
- private static final int EVENT_UID_RULES_CHANGED = 39;
-
- /**
- * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
- */
- private static final int EVENT_DATA_SAVER_CHANGED = 40;
-
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
* been tested.
@@ -559,8 +567,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
/**
- * used internally when setting the default networks for OemNetworkPreferences.
- * obj = OemNetworkPreferences
+ * Used internally when setting the default networks for OemNetworkPreferences.
+ * obj = Pair<OemNetworkPreferences, listener>
*/
private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
@@ -570,6 +578,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49;
/**
+ * Used internally when setting a network preference for a user profile.
+ * obj = Pair<ProfileNetworkPreference, Listener>
+ */
+ private static final int EVENT_SET_PROFILE_NETWORK_PREFERENCE = 50;
+
+ /**
+ * Event to specify that reasons for why an uid is blocked changed.
+ * arg1 = uid
+ * arg2 = blockedReasons
+ */
+ private static final int EVENT_UID_BLOCKED_REASON_CHANGED = 51;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -607,7 +628,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
private Intent mInitialBroadcast;
private PowerManager.WakeLock mNetTransitionWakeLock;
- private int mNetTransitionWakeLockTimeout;
private final PowerManager.WakeLock mPendingIntentWakeLock;
// A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
@@ -619,11 +639,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private UserManager mUserManager;
- private NetworkConfig[] mNetConfigs;
- private int mNetworksDefined;
-
// the set of network types that can only be enabled by system/sig apps
- private List mProtectedNetworks;
+ private List<Integer> mProtectedNetworks;
private Set<String> mWolSupportedInterfaces;
@@ -713,18 +730,63 @@ public class ConnectivityService extends IConnectivityManager.Stub
* They are therefore not thread-safe with respect to each other.
* - getNetworkForType() can be called at any time on binder threads. It is synchronized
* on mTypeLists to be thread-safe with respect to a concurrent remove call.
+ * - getRestoreTimerForType(type) is also synchronized on mTypeLists.
* - dump is thread-safe with respect to concurrent add and remove calls.
*/
private final ArrayList<NetworkAgentInfo> mTypeLists[];
@NonNull
private final ConnectivityService mService;
+ // Restore timers for requestNetworkForFeature (network type -> timer in ms). Types without
+ // an entry have no timer (equivalent to -1). Lazily loaded.
+ @NonNull
+ private ArrayMap<Integer, Integer> mRestoreTimers = new ArrayMap<>();
+
LegacyTypeTracker(@NonNull ConnectivityService service) {
mService = service;
mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
}
- public void addSupportedType(int type) {
+ public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
+ final PackageManager pm = ctx.getPackageManager();
+ if (pm.hasSystemFeature(FEATURE_WIFI)) {
+ addSupportedType(TYPE_WIFI);
+ }
+ if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
+ addSupportedType(TYPE_WIFI_P2P);
+ }
+ if (tm.isDataCapable()) {
+ // Telephony does not have granular support for these types: they are either all
+ // supported, or none is supported
+ addSupportedType(TYPE_MOBILE);
+ addSupportedType(TYPE_MOBILE_MMS);
+ addSupportedType(TYPE_MOBILE_SUPL);
+ addSupportedType(TYPE_MOBILE_DUN);
+ addSupportedType(TYPE_MOBILE_HIPRI);
+ addSupportedType(TYPE_MOBILE_FOTA);
+ addSupportedType(TYPE_MOBILE_IMS);
+ addSupportedType(TYPE_MOBILE_CBS);
+ addSupportedType(TYPE_MOBILE_IA);
+ addSupportedType(TYPE_MOBILE_EMERGENCY);
+ }
+ if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
+ addSupportedType(TYPE_BLUETOOTH);
+ }
+ if (pm.hasSystemFeature(FEATURE_WATCH)) {
+ // TYPE_PROXY is only used on Wear
+ addSupportedType(TYPE_PROXY);
+ }
+ // Ethernet is often not specified in the configs, although many devices can use it via
+ // USB host adapters. Add it as long as the ethernet service is here.
+ if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) {
+ addSupportedType(TYPE_ETHERNET);
+ }
+
+ // Always add TYPE_VPN as a supported type
+ addSupportedType(TYPE_VPN);
+ }
+
+ private void addSupportedType(int type) {
if (mTypeLists[type] != null) {
throw new IllegalStateException(
"legacy list for type " + type + "already initialized");
@@ -745,6 +807,35 @@ public class ConnectivityService extends IConnectivityManager.Stub
return null;
}
+ public int getRestoreTimerForType(int type) {
+ synchronized (mTypeLists) {
+ if (mRestoreTimers == null) {
+ mRestoreTimers = loadRestoreTimers();
+ }
+ return mRestoreTimers.getOrDefault(type, -1);
+ }
+ }
+
+ private ArrayMap<Integer, Integer> loadRestoreTimers() {
+ final String[] configs = mService.mResources.get().getStringArray(
+ com.android.connectivity.resources.R.array
+ .config_legacy_networktype_restore_timers);
+ final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length);
+ for (final String config : configs) {
+ final String[] splits = TextUtils.split(config, ",");
+ if (splits.length != 2) {
+ logwtf("Invalid restore timer token count: " + config);
+ continue;
+ }
+ try {
+ ret.put(Integer.parseInt(splits[0]), Integer.parseInt(splits[1]));
+ } catch (NumberFormatException e) {
+ logwtf("Invalid restore timer number format: " + config, e);
+ }
+ }
+ return ret;
+ }
+
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
boolean isDefaultNetwork) {
if (DBG) {
@@ -884,27 +975,59 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this);
+ final LocalPriorityDump mPriorityDumper = new LocalPriorityDump();
/**
* Helper class which parses out priority arguments and dumps sections according to their
* priority. If priority arguments are omitted, function calls the legacy dump command.
*/
- private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
- @Override
- public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
- doDump(fd, pw, new String[] {DIAG_ARG}, asProto);
- doDump(fd, pw, new String[] {SHORT_ARG}, asProto);
+ private class LocalPriorityDump {
+ private static final String PRIORITY_ARG = "--dump-priority";
+ private static final String PRIORITY_ARG_HIGH = "HIGH";
+ private static final String PRIORITY_ARG_NORMAL = "NORMAL";
+
+ LocalPriorityDump() {}
+
+ private void dumpHigh(FileDescriptor fd, PrintWriter pw) {
+ doDump(fd, pw, new String[] {DIAG_ARG});
+ doDump(fd, pw, new String[] {SHORT_ARG});
}
- @Override
- public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
- doDump(fd, pw, args, asProto);
+ private void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doDump(fd, pw, args);
}
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
- doDump(fd, pw, args, asProto);
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (args == null) {
+ dumpNormal(fd, pw, args);
+ return;
+ }
+
+ String priority = null;
+ for (int argIndex = 0; argIndex < args.length; argIndex++) {
+ if (args[argIndex].equals(PRIORITY_ARG) && argIndex + 1 < args.length) {
+ argIndex++;
+ priority = args[argIndex];
+ }
+ }
+
+ if (PRIORITY_ARG_HIGH.equals(priority)) {
+ dumpHigh(fd, pw);
+ } else if (PRIORITY_ARG_NORMAL.equals(priority)) {
+ dumpNormal(fd, pw, args);
+ } else {
+ // ConnectivityService publishes binder service using publishBinderService() with
+ // no priority assigned will be treated as NORMAL priority. Dumpsys does not send
+ // "--dump-priority" arguments to the service. Thus, dump both NORMAL and HIGH to
+ // align the legacy design.
+ // TODO: Integrate into signal dump.
+ dumpNormal(fd, pw, args);
+ pw.println();
+ pw.println("DUMP OF SERVICE HIGH connectivity");
+ pw.println();
+ dumpHigh(fd, pw);
+ }
}
- };
+ }
/**
* Keeps track of the number of requests made under different uids.
@@ -983,6 +1106,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
+ * Get the {@link ConnectivityResources} to use in ConnectivityService.
+ */
+ public ConnectivityResources getResources(@NonNull Context ctx) {
+ return new ConnectivityResources(ctx);
+ }
+
+ /**
* Create a HandlerThread to use in ConnectivityService.
*/
public HandlerThread makeHandlerThread() {
@@ -1035,19 +1165,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
return new MultinetworkPolicyTracker(c, h, r);
}
- public IBatteryStats getBatteryStatsService() {
- return BatteryStatsService.getService();
- }
-
/**
* @see BatteryStatsManager
*/
public void reportNetworkInterfaceForTransports(Context context, String iface,
int[] transportTypes) {
- final BatteryStatsManager batteryStats =
+ final BatteryStatsManager batteryStats =
context.getSystemService(BatteryStatsManager.class);
batteryStats.reportNetworkInterfaceForTransports(iface, transportTypes);
}
+
+ public boolean getCellular464XlatEnabled() {
+ return NetworkProperties.isCellular464XlatEnabled().orElse(true);
+ }
}
public ConnectivityService(Context context) {
@@ -1064,13 +1194,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
mSystemProperties = mDeps.getSystemProperties();
mNetIdManager = mDeps.makeNetIdManager();
mContext = Objects.requireNonNull(context, "missing Context");
+ mResources = deps.getResources(mContext);
mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
mMetricsLog = logger;
mNetworkRanker = new NetworkRanker();
final NetworkRequest defaultInternetRequest = createDefaultRequest();
mDefaultRequest = new NetworkRequestInfo(
- defaultInternetRequest, null, new Binder(),
+ defaultInternetRequest, null,
+ new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
null /* attributionTags */);
mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1116,86 +1248,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mLocationPermissionChecker = new LocationPermissionChecker(mContext);
- // To ensure uid rules are synchronized with Network Policy, register for
+ // To ensure uid state is synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
// reading existing policy from disk.
- mPolicyManager.registerListener(mPolicyListener);
+ mPolicyManager.registerNetworkPolicyCallback(null, mPolicyCallback);
final PowerManager powerManager = (PowerManager) context.getSystemService(
Context.POWER_SERVICE);
mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_networkTransitionTimeout);
mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
-
- // TODO: What is the "correct" way to do determine if this is a wifi only device?
- boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false);
- log("wifiOnly=" + wifiOnly);
- String[] naStrings = context.getResources().getStringArray(
- com.android.internal.R.array.networkAttributes);
- for (String naString : naStrings) {
- try {
- NetworkConfig n = new NetworkConfig(naString);
- if (VDBG) log("naString=" + naString + " config=" + n);
- if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
- loge("Error in networkAttributes - ignoring attempt to define type " +
- n.type);
- continue;
- }
- if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
- log("networkAttributes - ignoring mobile as this dev is wifiOnly " +
- n.type);
- continue;
- }
- if (mNetConfigs[n.type] != null) {
- loge("Error in networkAttributes - ignoring attempt to redefine type " +
- n.type);
- continue;
- }
- mLegacyTypeTracker.addSupportedType(n.type);
-
- mNetConfigs[n.type] = n;
- mNetworksDefined++;
- } catch(Exception e) {
- // ignore it - leave the entry null
- }
- }
-
- // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config.
- if (mNetConfigs[TYPE_VPN] == null) {
- // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we
- // don't need to add TYPE_VPN to mNetConfigs.
- mLegacyTypeTracker.addSupportedType(TYPE_VPN);
- mNetworksDefined++; // used only in the log() statement below.
- }
-
- // Do the same for Ethernet, since it's often not specified in the configs, although many
- // devices can use it via USB host adapters.
- if (mNetConfigs[TYPE_ETHERNET] == null
- && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
- mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
- mNetworksDefined++;
- }
-
- if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
-
- mProtectedNetworks = new ArrayList<Integer>();
+ mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager);
+ mProtectedNetworks = new ArrayList<>();
int[] protectedNetworks = context.getResources().getIntArray(
com.android.internal.R.array.config_protectedNetworks);
for (int p : protectedNetworks) {
- if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+ if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) {
mProtectedNetworks.add(p);
} else {
if (DBG) loge("Ignoring protectedNetwork " + p);
}
}
- mWolSupportedInterfaces = new ArraySet(
- mContext.getResources().getStringArray(
- com.android.internal.R.array.config_wakeonlan_supported_interfaces));
-
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1241,21 +1315,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDnsManager = new DnsManager(mContext, mDnsResolver);
registerPrivateDnsSettingsCallbacks();
+ // This NAI is a sentinel used to offer no service to apps that are on a multi-layer
+ // request that doesn't allow fallback to the default network. It should never be visible
+ // to apps. As such, it's not in the list of NAIs and doesn't need many of the normal
+ // arguments like the handler or the DnsResolver.
+ // TODO : remove this ; it is probably better handled with a sentinel request.
mNoServiceNetwork = new NetworkAgentInfo(null,
new Network(NO_SERVICE_NET_ID),
new NetworkInfo(TYPE_NONE, 0, "", ""),
- new LinkProperties(), new NetworkCapabilities(), 0, mContext,
- null, new NetworkAgentConfig(), this, null,
- null, 0, INVALID_UID,
- mQosCallbackTracker);
+ new LinkProperties(), new NetworkCapabilities(),
+ new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
+ new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker,
+ mDeps);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
+ return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
+ }
+
+ private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
+ @NonNull final UidRange uids) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
- netCap.setSingleUid(uid);
+ netCap.setUids(Collections.singleton(uids));
return netCap;
}
@@ -1327,7 +1411,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- networkRequest, null, new Binder(),
+ networkRequest, null,
+ new Binder(),
+ NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
null /* attributionTags */));
} else {
handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
@@ -1489,11 +1575,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
// but only exists if an app asks about them or requests them. Ensure the requesting app
// gets the type it asks for.
filtered.setType(type);
- final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)
- ? DetailedState.BLOCKED
- : filtered.getDetailedState();
- filtered.setDetailedState(getLegacyLockdownState(state),
- "" /* reason */, null /* extraInfo */);
+ if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
+ filtered.setDetailedState(DetailedState.BLOCKED, null /* reason */,
+ null /* extraInfo */);
+ }
+ filterForLegacyLockdown(filtered);
return filtered;
}
@@ -1569,8 +1655,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, false)
? DetailedState.BLOCKED
: DetailedState.DISCONNECTED;
- info.setDetailedState(getLegacyLockdownState(state),
- "" /* reason */, null /* extraInfo */);
+ info.setDetailedState(state, null /* reason */, null /* extraInfo */);
+ filterForLegacyLockdown(info);
return info;
}
@@ -1688,8 +1774,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
result.put(
nai.network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName,
- callingAttributionTag));
+ nc, false /* includeLocationSensitiveInfo */,
+ mDeps.getCallingUid(), callingPackageName, callingAttributionTag));
}
}
@@ -1702,7 +1788,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
result.put(
network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName,
+ nc,
+ false /* includeLocationSensitiveInfo */,
+ mDeps.getCallingUid(), callingPackageName,
callingAttributionTag));
}
}
@@ -1784,6 +1872,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceAccessPermission();
return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
getNetworkCapabilitiesInternal(network),
+ false /* includeLocationSensitiveInfo */,
mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
}
@@ -1817,8 +1906,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@VisibleForTesting
@Nullable
NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
- @Nullable String callingAttributionTag) {
+ @Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo,
+ int callerUid, @NonNull String callerPkgName, @Nullable String callingAttributionTag) {
if (nc == null) {
return null;
}
@@ -1826,7 +1915,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkCapabilities newNc;
// Avoid doing location permission check if the transport info has no location sensitive
// data.
- if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
+ if (includeLocationSensitiveInfo
+ && nc.getTransportInfo() != null
+ && nc.getTransportInfo().hasLocationSensitiveFields()) {
hasLocationPermission =
hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
newNc = new NetworkCapabilities(nc, hasLocationPermission);
@@ -1843,6 +1934,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Owner UIDs already checked above. No need to re-check.
return newNc;
}
+ // If the caller does not want location sensitive data & target SDK >= S, then mask info.
+ // Else include the owner UID iff the caller has location permission to provide backwards
+ // compatibility for older apps.
+ if (!includeLocationSensitiveInfo
+ && isTargetSdkAtleast(
+ Build.VERSION_CODES.S, callerUid, callerPkgName)) {
+ newNc.setOwnerUid(INVALID_UID);
+ return newNc;
+ }
+
if (hasLocationPermission == null) {
// Location permission not checked yet, check now for masking owner UID.
hasLocationPermission =
@@ -2131,53 +2232,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
- @Override
- public void onUidRulesChanged(int uid, int uidRules) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
- }
+ private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() {
@Override
- public void onRestrictBackgroundChanged(boolean restrictBackground) {
- // caller is NPMS, since we only register with them
- if (LOGD_BLOCKED_NETWORKINFO) {
- log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
- }
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+ public void onUidBlockedReasonChanged(int uid, int blockedReasons) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED,
+ uid, blockedReasons));
}
};
- void handleUidRulesChanged(int uid, int newRules) {
- // skip update when we've already applied rules
- final int oldRules = mUidRules.get(uid, RULE_NONE);
- if (oldRules == newRules) return;
-
- maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
-
- if (newRules == RULE_NONE) {
- mUidRules.delete(uid);
- } else {
- mUidRules.put(uid, newRules);
- }
- }
-
- void handleRestrictBackgroundChanged(boolean restrictBackground) {
- if (mRestrictBackground == restrictBackground) return;
-
- final List<UidRange> blockedRanges = mVpnBlockedUidRanges;
- for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
- final boolean curMetered = nai.networkCapabilities.isMetered();
- maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
- restrictBackground, blockedRanges, blockedRanges);
- }
-
- mRestrictBackground = restrictBackground;
- }
-
- private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
- boolean isBackgroundRestricted) {
- return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted);
+ void handleUidBlockedReasonChanged(int uid, int blockedReasons) {
+ maybeNotifyNetworkBlockedForNewState(uid, blockedReasons);
+ mUidBlockedReasons.put(uid, blockedReasons);
}
private boolean checkAnyPermissionOf(String... permissions) {
@@ -2364,9 +2429,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
}
- // Public because it's used by mLockdownTracker.
- public void sendConnectedBroadcast(NetworkInfo info) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ private void sendConnectedBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -2550,13 +2613,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
} catch (RemoteException | ServiceSpecificException e) {
loge("Can't set TCP buffer sizes:" + e);
}
-
- final Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TCP_DEFAULT_INIT_RWND,
- mSystemProperties.getInt("net.tcp.default_init_rwnd", 0));
- if (rwndValue != 0) {
- mSystemProperties.setTcpInitRwnd(rwndValue);
- }
}
@Override
@@ -2573,9 +2629,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// if the system property isn't set, use the value for the apn type
int ret = RESTORE_DEFAULT_NETWORK_DELAY;
- if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) &&
- (mNetConfigs[networkType] != null)) {
- ret = mNetConfigs[networkType].restoreTime;
+ if (mLegacyTypeTracker.isTypeSupported(networkType)) {
+ ret = mLegacyTypeTracker.getRestoreTimerForType(networkType);
}
return ret;
}
@@ -2603,7 +2658,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
- PriorityDump.dump(mPriorityDumper, fd, writer, args);
+ mPriorityDumper.dump(fd, writer, args);
}
private boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
@@ -2618,10 +2673,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void doDump(FileDescriptor fd, PrintWriter writer, String[] args, boolean asProto) {
+ private void doDump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
if (!checkDumpPermission(mContext, TAG, pw)) return;
- if (asProto) return;
if (CollectionUtils.contains(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
@@ -2662,19 +2716,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.decreaseIndent();
pw.println();
- pw.print("Restrict background: ");
- pw.println(mRestrictBackground);
- pw.println();
-
pw.println("Status for known UIDs:");
pw.increaseIndent();
- final int size = mUidRules.size();
+ final int size = mUidBlockedReasons.size();
for (int i = 0; i < size; i++) {
// Don't crash if the array is modified while dumping in bugreports.
try {
- final int uid = mUidRules.keyAt(i);
- final int uidRules = mUidRules.get(uid, RULE_NONE);
- pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+ final int uid = mUidBlockedReasons.keyAt(i);
+ final int blockedReasons = mUidBlockedReasons.valueAt(i);
+ pw.println("UID=" + uid + " blockedReasons="
+ + blockedReasonsToString(blockedReasons));
} catch (ArrayIndexOutOfBoundsException e) {
pw.println(" ArrayIndexOutOfBoundsException");
} catch (ConcurrentModificationException e) {
@@ -2864,22 +2915,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
super(looper);
}
- private boolean maybeHandleAsyncChannelMessage(Message msg) {
- switch (msg.what) {
- default:
- return false;
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
- handleAsyncChannelHalfConnect(msg);
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- handleAsyncChannelDisconnected(msg);
- break;
- }
- }
- return true;
- }
-
private void maybeHandleNetworkAgentMessage(Message msg) {
final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
final NetworkAgentInfo nai = arg.first;
@@ -2920,7 +2955,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
break;
}
case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
- updateNetworkScore(nai, msg.arg1);
+ updateNetworkScore(nai, (NetworkScore) arg.second);
break;
}
case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
@@ -3171,8 +3206,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public void handleMessage(Message msg) {
- if (!maybeHandleAsyncChannelMessage(msg)
- && !maybeHandleNetworkMonitorMessage(msg)
+ if (!maybeHandleNetworkMonitorMessage(msg)
&& !maybeHandleNetworkAgentInfoMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
@@ -3436,21 +3470,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return false;
}
- private void handleAsyncChannelHalfConnect(Message msg) {
- ensureRunningOnConnectivityServiceThread();
- if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- if (VDBG) log("NetworkFactory connected");
- // Finish setting up the full connection
- NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
- sendAllRequestsToProvider(npi);
- } else {
- loge("Error connecting NetworkFactory");
- mNetworkProviderInfos.remove(msg.obj);
- }
- }
- }
-
private void handleNetworkAgentRegistered(Message msg) {
final NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
if (!mNetworkAgentInfos.contains(nai)) {
@@ -3481,14 +3500,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- // This is a no-op if it's called with a message designating a provider that has
- // already been destroyed, because its reference will not be found in the relevant
- // maps.
- private void handleAsyncChannelDisconnected(Message msg) {
- NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
- if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
- }
-
// Destroys a network, remove references to it from the internal state managed by
// ConnectivityService, free its interfaces and clean up.
// Must be called on the Handler thread.
@@ -3651,6 +3662,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetworkRequestInfoLogs.log("REGISTER " + nri);
for (final NetworkRequest req : nri.mRequests) {
mNetworkRequests.put(req, nri);
+ // TODO: Consider update signal strength for other types.
if (req.isListen()) {
for (final NetworkAgentInfo network : mNetworkAgentInfos) {
if (req.networkCapabilities.hasSignalStrength()
@@ -3743,18 +3755,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
// listen requests won't keep up a network satisfying it. If this is not a multilayer
// request, return immediately. For multilayer requests, check to see if any of the
// multilayer requests may have a potential satisfier.
- if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
+ || nri.mRequests.get(0).isListenForBest())) {
return false;
}
for (final NetworkRequest req : nri.mRequests) {
// This multilayer listen request is satisfied therefore no further requests need to be
// evaluated deeming this network not a potential satisfier.
- if (req.isListen() && nri.getActiveRequest() == req) {
+ if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) {
return false;
}
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
- if (req.isListen()) {
+ if (req.isListen() || req.isListenForBest()) {
continue;
}
// If this Network is already the highest scoring Network for a request, or if
@@ -3794,8 +3807,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
? mNetworkRequests.get(request) : getNriForAppRequest(request);
if (nri != null) {
- if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
- && nri.mUid != callingUid) {
+ if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
log(String.format("UID %d attempted to %s for unowned request %s",
callingUid, requestedOperation, nri));
return null;
@@ -4420,7 +4432,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
break;
}
case EVENT_PROXY_HAS_CHANGED: {
- handleApplyDefaultProxy((ProxyInfo)msg.obj);
+ final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
+ handleApplyDefaultProxy(arg.second);
break;
}
case EVENT_REGISTER_NETWORK_PROVIDER: {
@@ -4508,22 +4521,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
handlePrivateDnsValidationUpdate(
(PrivateDnsValidationUpdate) msg.obj);
break;
- case EVENT_UID_RULES_CHANGED:
- handleUidRulesChanged(msg.arg1, msg.arg2);
- break;
- case EVENT_DATA_SAVER_CHANGED:
- handleRestrictBackgroundChanged(toBool(msg.arg1));
+ case EVENT_UID_BLOCKED_REASON_CHANGED:
+ handleUidBlockedReasonChanged(msg.arg1, msg.arg2);
break;
case EVENT_SET_REQUIRE_VPN_FOR_UIDS:
handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
break;
case EVENT_SET_OEM_NETWORK_PREFERENCE: {
- final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
- (Pair<OemNetworkPreferences,
- IOnSetOemNetworkPreferenceListener>) msg.obj;
+ final Pair<OemNetworkPreferences, IOnCompleteListener> arg =
+ (Pair<OemNetworkPreferences, IOnCompleteListener>) msg.obj;
handleSetOemNetworkPreference(arg.first, arg.second);
break;
}
+ case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
+ final Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener> arg =
+ (Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
+ msg.obj;
+ handleSetProfileNetworkPreference(arg.first, arg.second);
+ }
case EVENT_REPORT_NETWORK_ACTIVITY:
mNetworkActivityTracker.handleReportNetworkActivity();
break;
@@ -4597,7 +4612,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
mWakelockLogs.log("ACQUIRE for " + forWhom);
Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
- mHandler.sendMessageDelayed(msg, mNetTransitionWakeLockTimeout);
+ final int lockTimeout = mResources.get().getInteger(
+ com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+ mHandler.sendMessageDelayed(msg, lockTimeout);
}
// Called when we gain a new default network to release the network transition wakelock in a
@@ -4820,6 +4837,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
Log.wtf(TAG, s);
}
+ private static void logwtf(String s, Throwable t) {
+ Log.wtf(TAG, s, t);
+ }
+
private static void loge(String s) {
Log.e(TAG, s);
}
@@ -4974,8 +4995,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
final boolean curMetered = nai.networkCapabilities.isMetered();
- maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
- mRestrictBackground, mVpnBlockedUidRanges, newVpnBlockedUidRanges);
+ maybeNotifyNetworkBlocked(nai, curMetered, curMetered,
+ mVpnBlockedUidRanges, newVpnBlockedUidRanges);
}
mVpnBlockedUidRanges = newVpnBlockedUidRanges;
@@ -5012,8 +5033,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// The legacy lockdown VPN always uses the default network.
// If the VPN's underlying network is no longer the current default network, it means that
// the default network has just switched, and the VPN is about to disconnect.
- // Report that the VPN is not connected, so when the state of NetworkInfo objects
- // overwritten by getLegacyLockdownState will be set to CONNECTING and not CONNECTED.
+ // Report that the VPN is not connected, so the state of NetworkInfo objects overwritten
+ // by filterForLegacyLockdown will be set to CONNECTING and not CONNECTED.
final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
if (defaultNetwork == null || !defaultNetwork.network.equals(underlying[0])) {
return null;
@@ -5022,6 +5043,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nai;
};
+ // TODO: move all callers to filterForLegacyLockdown and delete this method.
+ // This likely requires making sendLegacyNetworkBroadcast take a NetworkInfo object instead of
+ // just a DetailedState object.
private DetailedState getLegacyLockdownState(DetailedState origState) {
if (origState != DetailedState.CONNECTED) {
return origState;
@@ -5031,6 +5055,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
: DetailedState.CONNECTED;
}
+ private void filterForLegacyLockdown(NetworkInfo ni) {
+ if (!mLockdownEnabled || !ni.isConnected()) return;
+ // The legacy lockdown VPN replaces the state of every network in CONNECTED state with the
+ // state of its VPN. This is to ensure that when an underlying network connects, apps will
+ // not see a CONNECTIVITY_ACTION broadcast for a network in state CONNECTED until the VPN
+ // comes up, at which point there is a new CONNECTIVITY_ACTION broadcast for the underlying
+ // network, this time with a state of CONNECTED.
+ //
+ // Now that the legacy lockdown code lives in ConnectivityService, and no longer has access
+ // to the internal state of the Vpn object, always replace the state with CONNECTING. This
+ // is not too far off the truth, since an always-on VPN, when not connected, is always
+ // trying to reconnect.
+ if (getLegacyLockdownNai() == null) {
+ ni.setDetailedState(DetailedState.CONNECTING, "", null);
+ }
+ }
+
@Override
public void setProvisioningNotificationVisible(boolean visible, int networkType,
String action) {
@@ -5072,6 +5113,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onUserRemoved(UserHandle user) {
mPermissionMonitor.onUserRemoved(user);
+ // If there was a network preference for this user, remove it.
+ handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
+ null /* listener */);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
}
@@ -5109,8 +5153,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final IBinder.DeathRecipient mDeathRecipient;
public final int providerId;
- NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
+ NetworkProviderInfo(String name, Messenger messenger, int providerId,
+ @NonNull IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
this.providerId = providerId;
@@ -5204,6 +5248,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final IBinder mBinder;
final int mPid;
final int mUid;
+ final @NetworkCallback.Flag int mCallbackFlags;
@Nullable
final String mCallingAttributionTag;
// In order to preserve the mapping of NetworkRequest-to-callback when apps register
@@ -5251,17 +5296,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ /**
+ * Location sensitive data not included in pending intent. Only included in
+ * {@link NetworkCallback}.
+ */
+ mCallbackFlags = NetworkCallback.FLAG_NONE;
mCallingAttributionTag = callingAttributionTag;
}
NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m,
- @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
- this(Collections.singletonList(r), r, m, binder, callingAttributionTag);
+ @Nullable final IBinder binder,
+ @NetworkCallback.Flag int callbackFlags,
+ @Nullable String callingAttributionTag) {
+ this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag);
}
NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
@NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m,
- @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
+ @Nullable final IBinder binder,
+ @NetworkCallback.Flag int callbackFlags,
+ @Nullable String callingAttributionTag) {
super();
ensureAllNetworkRequestsHaveType(r);
mRequests = initializeRequests(r);
@@ -5272,6 +5326,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mUid = mDeps.getCallingUid();
mPendingIntent = null;
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ mCallbackFlags = callbackFlags;
mCallingAttributionTag = callingAttributionTag;
try {
@@ -5313,6 +5368,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mUid = nri.mUid;
mPendingIntent = nri.mPendingIntent;
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ mCallbackFlags = nri.mCallbackFlags;
mCallingAttributionTag = nri.mCallingAttributionTag;
}
@@ -5362,7 +5418,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
+ " callback request Id: "
+ mNetworkRequestForCallback.requestId
+ " " + mRequests
- + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+ + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
+ + "callback flags: " + mCallbackFlags;
}
}
@@ -5446,13 +5503,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
- final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
+ private boolean isTargetSdkAtleast(int version, int callingUid,
+ @NonNull String callingPackageName) {
+ final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
final PackageManager pm =
mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
try {
- final int callingVersion = pm.getApplicationInfo(
- callingPackageName, 0 /* flags */).targetSdkVersion;
+ final int callingVersion = pm.getTargetSdkVersion(callingPackageName);
if (callingVersion < version) return false;
} catch (PackageManager.NameNotFoundException e) { }
return true;
@@ -5461,10 +5518,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
- int legacyType, @NonNull String callingPackageName,
+ int legacyType, int callbackFlags, @NonNull String callingPackageName,
@Nullable String callingAttributionTag) {
if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
- if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+ if (isTargetSdkAtleast(Build.VERSION_CODES.M, mDeps.getCallingUid(),
+ callingPackageName)) {
throw new SecurityException("Insufficient permissions to specify legacy type");
}
}
@@ -5501,6 +5559,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// request if the app changes network state. http://b/29964605
enforceMeteredApnPolicy(networkCapabilities);
break;
+ case LISTEN_FOR_BEST:
+ enforceAccessPermission();
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ break;
default:
throw new IllegalArgumentException("Unsupported request type " + reqType);
}
@@ -5508,11 +5570,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), callingUid, callingPackageName);
- // Set the UID range for this request to the single UID of the requester, or to an empty
- // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
+ // Enforce FOREGROUND if the caller does not have permission to use background network.
+ if (reqType == LISTEN_FOR_BEST) {
+ restrictBackgroundRequestForCaller(networkCapabilities);
+ }
+
+ // Set the UID range for this request to the single UID of the requester, unless the
+ // requester has the permission to specify other UIDs.
// This will overwrite any allowed UIDs in the requested capabilities. Though there
// are no visible methods to set the UIDs, an app could use reflection to try and get
// networks for other apps so it's essential that the UIDs are overwritten.
+ // Also set the requester UID and package name in the request.
restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
callingUid, callingPackageName);
@@ -5524,7 +5592,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), reqType);
final NetworkRequestInfo nri = getNriToRegister(
- networkRequest, messenger, binder, callingAttributionTag);
+ networkRequest, messenger, binder, callbackFlags, callingAttributionTag);
if (DBG) log("requestNetwork for " + nri);
// For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
@@ -5559,6 +5627,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr,
@Nullable final Messenger msgr, @Nullable final IBinder binder,
+ @NetworkCallback.Flag int callbackFlags,
@Nullable String callingAttributionTag) {
final List<NetworkRequest> requests;
if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) {
@@ -5567,7 +5636,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
requests = Collections.singletonList(nr);
}
- return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag);
+ return new NetworkRequestInfo(
+ requests, nr, msgr, binder, callbackFlags, callingAttributionTag);
}
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
@@ -5693,8 +5763,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, IBinder binder, @NonNull String callingPackageName,
- @Nullable String callingAttributionTag) {
+ Messenger messenger, IBinder binder,
+ @NetworkCallback.Flag int callbackFlags,
+ @NonNull String callingPackageName, @NonNull String callingAttributionTag) {
final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
@@ -5715,7 +5786,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
NetworkRequestInfo nri =
- new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag);
+ new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags,
+ callingAttributionTag);
if (VDBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5784,8 +5856,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
public int registerNetworkProvider(Messenger messenger, String name) {
enforceNetworkFactoryOrSettingsPermission();
NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
- null /* asyncChannel */, nextNetworkProviderId(),
- () -> unregisterNetworkProvider(messenger));
+ nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
return npi.providerId;
}
@@ -5843,10 +5914,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
- // Current OEM network preferences.
+ // Current OEM network preferences. This object must only be written to on the handler thread.
+ // Since it is immutable and always non-null, other threads may read it if they only care
+ // about seeing a consistent object but not that it is current.
@NonNull
private OemNetworkPreferences mOemNetworkPreferences =
new OemNetworkPreferences.Builder().build();
+ // Current per-profile network preferences. This object follows the same threading rules as
+ // the OEM network preferences above.
+ @NonNull
+ private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
// The always-on request for an Internet-capable network that apps without a specific default
// fall back to.
@@ -6036,20 +6113,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nai == getDefaultNetwork();
}
- // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
- // changes that would conflict throughout the automerger graph. Having this method temporarily
- // helps with the process of going through with all these dependent changes across the entire
- // tree.
- /**
- * Register a new agent. {@see #registerNetworkAgent} below.
- */
- public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
- LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkAgentConfig networkAgentConfig) {
- return registerNetworkAgent(na, networkInfo, linkProperties, networkCapabilities,
- currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
- }
-
/**
* Register a new agent with ConnectivityService to handle a network.
*
@@ -6060,7 +6123,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
* later : see {@link #updateLinkProperties}.
* @param networkCapabilities the initial capabilites of this network. They can be updated
* later : see {@link #updateCapabilities}.
- * @param currentScore the initial score of the network. See
+ * @param initialScore the initial score of the network. See
* {@link NetworkAgentInfo#getCurrentScore}.
* @param networkAgentConfig metadata about the network. This is never updated.
* @param providerId the ID of the provider owning this NetworkAgent.
@@ -6068,10 +6131,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+ @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig,
+ int providerId) {
Objects.requireNonNull(networkInfo, "networkInfo must not be null");
Objects.requireNonNull(linkProperties, "linkProperties must not be null");
Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+ Objects.requireNonNull(initialScore, "initialScore must not be null");
Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null");
if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
@@ -6083,7 +6148,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final long token = Binder.clearCallingIdentity();
try {
return registerNetworkAgentInternal(na, networkInfo, linkProperties,
- networkCapabilities, currentScore, networkAgentConfig, providerId, uid);
+ networkCapabilities, initialScore, networkAgentConfig, providerId, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -6091,7 +6156,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) {
+ NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId,
+ int uid) {
if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
// Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
// the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
@@ -6106,7 +6172,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
- this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker);
+ this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc);
@@ -6472,6 +6538,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateWakeOnLan(@NonNull LinkProperties lp) {
+ if (mWolSupportedInterfaces == null) {
+ mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
+ com.android.connectivity.resources.R.array
+ .config_wakeonlan_supported_interfaces));
+ }
lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
}
@@ -6708,8 +6779,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final boolean meteredChanged = oldMetered != newMetered;
if (meteredChanged) {
- maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
- mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges);
+ maybeNotifyNetworkBlocked(nai, oldMetered, newMetered,
+ mVpnBlockedUidRanges, mVpnBlockedUidRanges);
}
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
@@ -7044,6 +7115,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
putParcelable(bundle, networkAgent.network);
}
+ final boolean includeLocationSensitiveInfo =
+ (nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
switch (notificationType) {
case ConnectivityManager.CALLBACK_AVAILABLE: {
final NetworkCapabilities nc =
@@ -7052,7 +7125,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+ nc, includeLocationSensitiveInfo, nri.mUid,
+ nrForCallback.getRequestorPackageName(),
nri.mCallingAttributionTag));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -7072,7 +7146,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+ netCap, includeLocationSensitiveInfo, nri.mUid,
+ nrForCallback.getRequestorPackageName(),
nri.mCallingAttributionTag));
break;
}
@@ -7806,7 +7881,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) {
+ private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final NetworkScore score) {
if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
nai.setScore(score);
rematchAllNetworksAndRequests();
@@ -7828,8 +7903,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final boolean metered = nai.networkCapabilities.isMetered();
boolean blocked;
blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges);
- blocked |= isUidBlockedByRules(nri.mUid, mUidRules.get(nri.mUid),
- metered, mRestrictBackground);
+ blocked |= NetworkPolicyManager.isUidBlocked(
+ mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE), metered);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
}
@@ -7847,16 +7922,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
*
* @param nai The target NetworkAgentInfo.
* @param oldMetered True if the previous network capabilities is metered.
- * @param newRestrictBackground True if data saver is enabled.
*/
private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
- boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground,
- List<UidRange> oldBlockedUidRanges, List<UidRange> newBlockedUidRanges) {
+ boolean newMetered, List<UidRange> oldBlockedUidRanges,
+ List<UidRange> newBlockedUidRanges) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
- final int uidRules = mUidRules.get(nri.mUid);
final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges);
@@ -7864,10 +7937,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges)
: oldVpnBlocked;
- oldBlocked = oldVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, oldMetered,
- oldRestrictBackground);
- newBlocked = newVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, newMetered,
- newRestrictBackground);
+ final int blockedReasons = mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE);
+ oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked(
+ blockedReasons, oldMetered);
+ newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked(
+ blockedReasons, newMetered);
if (oldBlocked != newBlocked) {
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
@@ -7877,19 +7951,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Notify apps with a given UID of the new blocked state according to new uid rules.
+ * Notify apps with a given UID of the new blocked state according to new uid state.
* @param uid The uid for which the rules changed.
- * @param newRules The new rules to apply.
+ * @param blockedReasons The reasons for why an uid is blocked.
*/
- private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+ private void maybeNotifyNetworkBlockedForNewState(int uid, int blockedReasons) {
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
final boolean metered = nai.networkCapabilities.isMetered();
final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
final boolean oldBlocked, newBlocked;
- oldBlocked = vpnBlocked || isUidBlockedByRules(
- uid, mUidRules.get(uid), metered, mRestrictBackground);
- newBlocked = vpnBlocked || isUidBlockedByRules(
- uid, newRules, metered, mRestrictBackground);
+
+ oldBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
+ mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered);
+ newBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
+ blockedReasons, metered);
if (oldBlocked == newBlocked) {
continue;
}
@@ -7914,6 +7989,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// and is still connected.
NetworkInfo info = new NetworkInfo(nai.networkInfo);
info.setType(type);
+ filterForLegacyLockdown(info);
if (state != DetailedState.DISCONNECTED) {
info.setDetailedState(state, null, info.getExtraInfo());
sendConnectedBroadcast(info);
@@ -8028,8 +8104,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public String getCaptivePortalServerUrl() {
enforceNetworkStackOrSettingsPermission();
- String settingUrl = mContext.getResources().getString(
- R.string.config_networkCaptivePortalServerUrl);
+ String settingUrl = mResources.get().getString(
+ com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
if (!TextUtils.isEmpty(settingUrl)) {
return settingUrl;
@@ -8122,7 +8198,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
+ ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null);
}
@Override
@@ -8827,13 +8903,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
private int transportTypeToLegacyType(int type) {
switch (type) {
case NetworkCapabilities.TRANSPORT_CELLULAR:
- return ConnectivityManager.TYPE_MOBILE;
+ return TYPE_MOBILE;
case NetworkCapabilities.TRANSPORT_WIFI:
- return ConnectivityManager.TYPE_WIFI;
+ return TYPE_WIFI;
case NetworkCapabilities.TRANSPORT_BLUETOOTH:
- return ConnectivityManager.TYPE_BLUETOOTH;
+ return TYPE_BLUETOOTH;
case NetworkCapabilities.TRANSPORT_ETHERNET:
- return ConnectivityManager.TYPE_ETHERNET;
+ return TYPE_ETHERNET;
default:
loge("Unexpected transport in transportTypeToLegacyType: " + type);
}
@@ -9048,6 +9124,143 @@ public class ConnectivityService extends IConnectivityManager.Stub
mQosCallbackTracker.unregisterCallback(callback);
}
+ // Network preference per-profile and OEM network preferences can't be set at the same
+ // time, because it is unclear what should happen if both preferences are active for
+ // one given UID. To make it possible, the stack would have to clarify what would happen
+ // in case both are active at the same time. The implementation may have to be adjusted
+ // to implement the resulting rules. For example, a priority could be defined between them,
+ // where the OEM preference would be considered less or more important than the enterprise
+ // preference ; this would entail implementing the priorities somehow, e.g. by doing
+ // UID arithmetic with UID ranges or passing a priority to netd so that the routing rules
+ // are set at the right level. Other solutions are possible, e.g. merging of the
+ // preferences for the relevant UIDs.
+ private static void throwConcurrentPreferenceException() {
+ throw new IllegalStateException("Can't set NetworkPreferenceForUser and "
+ + "set OemNetworkPreference at the same time");
+ }
+
+ /**
+ * Request that a user profile is put by default on a network matching a given preference.
+ *
+ * See the documentation for the individual preferences for a description of the supported
+ * behaviors.
+ *
+ * @param profile the profile concerned.
+ * @param preference the preference for this profile, as one of the PROFILE_NETWORK_PREFERENCE_*
+ * constants.
+ * @param listener an optional listener to listen for completion of the operation.
+ */
+ @Override
+ public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+ @ConnectivityManager.ProfileNetworkPreference final int preference,
+ @Nullable final IOnCompleteListener listener) {
+ Objects.requireNonNull(profile);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (DBG) {
+ log("setProfileNetworkPreference " + profile + " to " + preference);
+ }
+ if (profile.getIdentifier() < 0) {
+ throw new IllegalArgumentException("Must explicitly specify a user handle ("
+ + "UserHandle.CURRENT not supported)");
+ }
+ final UserManager um;
+ try {
+ um = mContext.createContextAsUser(profile, 0 /* flags */)
+ .getSystemService(UserManager.class);
+ } catch (IllegalStateException e) {
+ throw new IllegalArgumentException("Profile does not exist");
+ }
+ if (!um.isManagedProfile()) {
+ throw new IllegalArgumentException("Profile must be a managed profile");
+ }
+ // Strictly speaking, mOemNetworkPreferences should only be touched on the
+ // handler thread. However it is an immutable object, so reading the reference is
+ // safe - it's just possible the value is slightly outdated. For the final check,
+ // see #handleSetProfileNetworkPreference. But if this can be caught here it is a
+ // lot easier to understand, so opportunistically check it.
+ if (!mOemNetworkPreferences.isEmpty()) {
+ throwConcurrentPreferenceException();
+ }
+ final NetworkCapabilities nc;
+ switch (preference) {
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
+ nc = null;
+ break;
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+ final UidRange uids = UidRange.createForUser(profile);
+ nc = createDefaultNetworkCapabilitiesForUidRange(uids);
+ nc.addCapability(NET_CAPABILITY_ENTERPRISE);
+ nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid preference in setProfileNetworkPreference");
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
+ new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener)));
+ }
+
+ private void validateNetworkCapabilitiesOfProfileNetworkPreference(
+ @Nullable final NetworkCapabilities nc) {
+ if (null == nc) return; // Null caps are always allowed. It means to remove the setting.
+ ensureRequestableCapabilities(nc);
+ }
+
+ private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
+ @NonNull final ProfileNetworkPreferences prefs) {
+ final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
+ for (final ProfileNetworkPreferences.Preference pref : prefs.preferences) {
+ // The NRI for a user should be comprised of two layers:
+ // - The request for the capabilities
+ // - The request for the default network, for fallback. Create an image of it to
+ // have the correct UIDs in it (also a request can only be part of one NRI, because
+ // of lookups in 1:1 associations like mNetworkRequests).
+ // Note that denying a fallback can be implemented simply by not adding the second
+ // request.
+ final ArrayList<NetworkRequest> nrs = new ArrayList<>();
+ nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities));
+ nrs.add(createDefaultRequest());
+ setNetworkRequestUids(nrs, pref.capabilities.getUids());
+ final NetworkRequestInfo nri = new NetworkRequestInfo(nrs);
+ result.add(nri);
+ }
+ return result;
+ }
+
+ private void handleSetProfileNetworkPreference(
+ @NonNull final ProfileNetworkPreferences.Preference preference,
+ @Nullable final IOnCompleteListener listener) {
+ // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+ // particular because it's not clear what preference should win in case both apply
+ // to the same app.
+ // The binder call has already checked this, but as mOemNetworkPreferences is only
+ // touched on the handler thread, it's theoretically not impossible that it has changed
+ // since.
+ if (!mOemNetworkPreferences.isEmpty()) {
+ // This may happen on a device with an OEM preference set when a user is removed.
+ // In this case, it's safe to ignore. In particular this happens in the tests.
+ loge("handleSetProfileNetworkPreference, but OEM network preferences not empty");
+ return;
+ }
+
+ validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
+
+ mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
+ final ArraySet<NetworkRequestInfo> nris =
+ createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
+ replaceDefaultNetworkRequestsForPreference(nris);
+ // Finally, rematch.
+ rematchAllNetworksAndRequests();
+
+ if (null != listener) {
+ try {
+ listener.onComplete();
+ } catch (RemoteException e) {
+ loge("Listener for setProfileNetworkPreference has died");
+ }
+ }
+ }
+
private void enforceAutomotiveDevice() {
final boolean isAutomotiveDevice =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
@@ -9066,17 +9279,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
* Calling this will overwrite the existing preference.
*
* @param preference {@link OemNetworkPreferences} The application network preference to be set.
- * @param listener {@link ConnectivityManager.OnSetOemNetworkPreferenceListener} Listener used
+ * @param listener {@link ConnectivityManager.OnCompleteListener} Listener used
* to communicate completion of setOemNetworkPreference();
*/
@Override
public void setOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
- @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+ @Nullable final IOnCompleteListener listener) {
enforceAutomotiveDevice();
enforceOemNetworkPreferencesPermission();
+ if (!mProfileNetworkPreferences.isEmpty()) {
+ // Strictly speaking, mProfileNetworkPreferences should only be touched on the
+ // handler thread. However it is an immutable object, so reading the reference is
+ // safe - it's just possible the value is slightly outdated. For the final check,
+ // see #handleSetOemPreference. But if this can be caught here it is a
+ // lot easier to understand, so opportunistically check it.
+ throwConcurrentPreferenceException();
+ }
+
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
validateOemNetworkPreferences(preference);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
@@ -9095,11 +9317,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleSetOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
- @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+ @Nullable final IOnCompleteListener listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
if (DBG) {
log("set OEM network preferences :" + preference.toString());
}
+ // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+ // particular because it's not clear what preference should win in case both apply
+ // to the same app.
+ // The binder call has already checked this, but as mOemNetworkPreferences is only
+ // touched on the handler thread, it's theoretically not impossible that it has changed
+ // since.
+ if (!mProfileNetworkPreferences.isEmpty()) {
+ logwtf("handleSetOemPreference, but per-profile network preferences not empty");
+ return;
+ }
+
final ArraySet<NetworkRequestInfo> nris =
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
replaceDefaultNetworkRequestsForPreference(nris);
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index b9922087109f..2465479aadd8 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -16,9 +16,6 @@
package com.android.server;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
import android.content.Context;
import android.util.Log;
@@ -42,6 +39,6 @@ public final class ConnectivityServiceInitializer extends SystemService {
public void onStart() {
Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
- /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+ /* allowIsolated= */ false);
}
}
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index a35351958f43..96ff9006acce 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.Log;
@@ -51,4 +53,9 @@ class ContextHubSystemService extends SystemService {
publishBinderService(Context.CONTEXTHUB_SERVICE, mContextHubService);
}
}
+
+ @Override
+ public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
+ mContextHubService.onUserChanged();
+ }
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 81d4b9da63c8..4c3c6ef21fc5 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -56,6 +56,7 @@ import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Range;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -756,13 +757,9 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- // These values have been reserved in NetIdManager
- @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
-
- public static final int TUN_INTF_NETID_RANGE = 0x0400;
-
private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
- private int mNextTunnelNetIdIndex = 0;
+ final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
+ private int mNextTunnelNetId = mNetIdRange.getLower();
/**
* Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
@@ -775,11 +772,13 @@ public class IpSecService extends IIpSecService.Stub {
*/
@VisibleForTesting
int reserveNetId() {
+ final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
synchronized (mTunnelNetIds) {
- for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
- int index = mNextTunnelNetIdIndex;
- int netId = index + TUN_INTF_NETID_START;
- if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+ for (int i = 0; i < range; i++) {
+ final int netId = mNextTunnelNetId;
+ if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
+ mNextTunnelNetId = mNetIdRange.getLower();
+ }
if (!mTunnelNetIds.get(netId)) {
mTunnelNetIds.put(netId, true);
return netId;
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
index 097fb3ae47e3..61925c80a22b 100644
--- a/services/core/java/com/android/server/NetIdManager.java
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.annotation.NonNull;
+import android.net.ConnectivityManager;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
@@ -31,7 +32,7 @@ public class NetIdManager {
// Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
public static final int MIN_NET_ID = 100; // some reserved marks
// Top IDs reserved by IpSecService
- public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+ public static final int MAX_NET_ID = ConnectivityManager.getIpSecNetIdRange().getLower() - 1;
@GuardedBy("mNetIdInUse")
private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index e12586bfdc06..c9836001da77 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -18,6 +18,7 @@ per-file ServiceWatcher.java = sooniln@google.com
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *Battery* = file:/BATTERY_STATS_OWNERS
per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index d10cf4dd0505..fa3771ac70fa 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,6 +23,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -104,6 +105,9 @@ import java.util.concurrent.TimeUnit;
public class PersistentDataBlockService extends SystemService {
private static final String TAG = PersistentDataBlockService.class.getSimpleName();
+ private static final String GSI_SANDBOX = "/data/gsi_persistent_data";
+ private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
@@ -128,12 +132,13 @@ public class PersistentDataBlockService extends SystemService {
private static final String FLASH_LOCK_UNLOCKED = "0";
private final Context mContext;
- private final String mDataBlockFile;
+ private final boolean mIsRunningDSU;
private final Object mLock = new Object();
private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
private int mAllowedUid = -1;
private long mBlockDeviceSize;
+ private String mDataBlockFile;
@GuardedBy("mLock")
private boolean mIsWritable = true;
@@ -142,6 +147,7 @@ public class PersistentDataBlockService extends SystemService {
super(context);
mContext = context;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+ mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
mBlockDeviceSize = -1; // Load lazily
}
@@ -286,14 +292,28 @@ public class PersistentDataBlockService extends SystemService {
return true;
}
+ private FileOutputStream getBlockOutputStream() throws IOException {
+ if (!mIsRunningDSU) {
+ return new FileOutputStream(new File(mDataBlockFile));
+ } else {
+ File sandbox = new File(GSI_SANDBOX);
+ File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
+ if (!sandbox.exists()) {
+ FileUtils.copy(realpdb, sandbox);
+ mDataBlockFile = GSI_SANDBOX;
+ }
+ Slog.i(TAG, "PersistentDataBlock copy-on-write");
+ return new FileOutputStream(sandbox);
+ }
+ }
+
private boolean computeAndWriteDigestLocked() {
byte[] digest = computeDigestLocked(null);
if (digest != null) {
DataOutputStream outputStream;
try {
- outputStream = new DataOutputStream(
- new FileOutputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
+ outputStream = new DataOutputStream(getBlockOutputStream());
+ } catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return false;
}
@@ -358,8 +378,8 @@ public class PersistentDataBlockService extends SystemService {
private void formatPartitionLocked(boolean setOemUnlockEnabled) {
DataOutputStream outputStream;
try {
- outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
+ outputStream = new DataOutputStream(getBlockOutputStream());
+ } catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return;
}
@@ -384,8 +404,8 @@ public class PersistentDataBlockService extends SystemService {
private void doSetOemUnlockEnabledLocked(boolean enabled) {
FileOutputStream outputStream;
try {
- outputStream = new FileOutputStream(new File(mDataBlockFile));
- } catch (FileNotFoundException e) {
+ outputStream = getBlockOutputStream();
+ } catch (IOException e) {
Slog.e(TAG, "partition not available", e);
return;
}
@@ -461,8 +481,8 @@ public class PersistentDataBlockService extends SystemService {
DataOutputStream outputStream;
try {
- outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
+ outputStream = new DataOutputStream(getBlockOutputStream());
+ } catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return -1;
}
@@ -547,6 +567,17 @@ public class PersistentDataBlockService extends SystemService {
public void wipe() {
enforceOemUnlockWritePermission();
+ if (mIsRunningDSU) {
+ File sandbox = new File(GSI_SANDBOX);
+ if (sandbox.exists()) {
+ if (sandbox.delete()) {
+ mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+ } else {
+ Slog.e(TAG, "Failed to wipe sandbox persistent data block");
+ }
+ }
+ return;
+ }
synchronized (mLock) {
int ret = nativeWipe(mDataBlockFile);
@@ -706,8 +737,8 @@ public class PersistentDataBlockService extends SystemService {
private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
FileOutputStream outputStream;
try {
- outputStream = new FileOutputStream(new File(mDataBlockFile));
- } catch (FileNotFoundException e) {
+ outputStream = getBlockOutputStream();
+ } catch (IOException e) {
Slog.e(TAG, "partition not available", e);
return;
}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index f2782f64995a..18d47c6fb7b3 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -51,6 +51,7 @@ import android.graphics.drawable.Icon;
import android.hardware.ISensorPrivacyListener;
import android.hardware.ISensorPrivacyManager;
import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -67,6 +68,7 @@ import android.service.SensorPrivacyServiceDumpProto;
import android.service.SensorPrivacyUserProto;
import android.text.Html;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -80,6 +82,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.XmlUtils;
@@ -137,6 +140,8 @@ public final class SensorPrivacyService extends SystemService {
private final ActivityManager mActivityManager;
private final ActivityTaskManager mActivityTaskManager;
+ private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
+
public SensorPrivacyService(Context context) {
super(context);
mUserManagerInternal = getLocalService(UserManagerInternal.class);
@@ -148,6 +153,9 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.SENSOR_PRIVACY_SERVICE, mSensorPrivacyServiceImpl);
+ mSensorPrivacyManagerInternal = new SensorPrivacyManagerInternalImpl();
+ publishLocalService(SensorPrivacyManagerInternal.class,
+ mSensorPrivacyManagerInternal);
}
class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements
@@ -1108,6 +1116,7 @@ public final class SensorPrivacyService extends SystemService {
}
public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+ mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
mIndividualSensorListeners.get(userId);
if (listenersForUser == null) {
@@ -1168,4 +1177,87 @@ public final class SensorPrivacyService extends SystemService {
c.accept(userIds[i]);
}
}
+
+ private class SensorPrivacyManagerInternalImpl extends SensorPrivacyManagerInternal {
+
+ private ArrayMap<Integer, ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>>>
+ mListeners = new ArrayMap<>();
+ private ArrayMap<Integer, ArraySet<OnUserSensorPrivacyChangedListener>> mAllUserListeners =
+ new ArrayMap<>();
+
+ private final Object mLock = new Object();
+
+ private void dispatch(int userId, int sensor, boolean enabled) {
+ synchronized (mLock) {
+ ArraySet<OnUserSensorPrivacyChangedListener> allUserSensorListeners =
+ mAllUserListeners.get(sensor);
+ if (allUserSensorListeners != null) {
+ for (int i = 0; i < allUserSensorListeners.size(); i++) {
+ OnUserSensorPrivacyChangedListener listener =
+ allUserSensorListeners.valueAt(i);
+ BackgroundThread.getHandler().post(() ->
+ listener.onSensorPrivacyChanged(userId, enabled));
+ }
+ }
+
+ ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners =
+ mListeners.get(userId);
+ if (userSensorListeners != null) {
+ ArraySet<OnSensorPrivacyChangedListener> sensorListeners =
+ userSensorListeners.get(sensor);
+ if (sensorListeners != null) {
+ for (int i = 0; i < sensorListeners.size(); i++) {
+ OnSensorPrivacyChangedListener listener = sensorListeners.valueAt(i);
+ BackgroundThread.getHandler().post(() ->
+ listener.onSensorPrivacyChanged(enabled));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isSensorPrivacyEnabled(int userId, int sensor) {
+ return SensorPrivacyService.this
+ .mSensorPrivacyServiceImpl.isIndividualSensorPrivacyEnabled(userId, sensor);
+ }
+
+ @Override
+ public void addSensorPrivacyListener(int userId, int sensor,
+ OnSensorPrivacyChangedListener listener) {
+ synchronized (mLock) {
+ ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners =
+ mListeners.get(userId);
+ if (userSensorListeners == null) {
+ userSensorListeners = new ArrayMap<>();
+ mListeners.put(userId, userSensorListeners);
+ }
+
+ ArraySet<OnSensorPrivacyChangedListener> sensorListeners =
+ userSensorListeners.get(sensor);
+ if (sensorListeners == null) {
+ sensorListeners = new ArraySet<>();
+ userSensorListeners.put(sensor, sensorListeners);
+ }
+
+ sensorListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void addSensorPrivacyListenerForAllUsers(int sensor,
+ OnUserSensorPrivacyChangedListener listener) {
+ synchronized (mLock) {
+ ArraySet<OnUserSensorPrivacyChangedListener> sensorListeners =
+ mAllUserListeners.get(sensor);
+ if (sensorListeners == null) {
+ sensorListeners = new ArraySet<>();
+ mAllUserListeners.put(sensor, sensorListeners);
+ }
+
+ sensorListeners.add(listener);
+ }
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 27b648e53a38..8b5bb1de4d1b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -962,7 +962,7 @@ class StorageManagerService extends IStorageManager.Stub
}
int delay = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
- ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 0);
+ ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 5000);
Slog.v(TAG, "getAnrDelayMillis for " + packageName + ". " + delay + "ms");
return delay;
}
@@ -1791,7 +1791,7 @@ class StorageManagerService extends IStorageManager.Stub
public StorageManagerService(Context context) {
sSelf = this;
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
@@ -4553,6 +4553,13 @@ class StorageManagerService extends IStorageManager.Stub
private final List<StorageManagerInternal.ResetListener> mResetListeners =
new ArrayList<>();
+ @Override
+ public boolean isFuseMounted(int userId) {
+ synchronized (mLock) {
+ return mFuseMountedUser.contains(userId);
+ }
+ }
+
/**
* Check if fuse is running in target user, if it's running then setup its storage dirs.
* Return true if storage dirs are mounted.
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 95af84293377..a09dbc7e599d 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -21,7 +21,9 @@
}
],
"file_patterns": ["NotificationManagerService\\.java"]
- },
+ }
+ ],
+ "presubmit-large": [
{
"name": "CtsScopedStorageCoreHostTest",
"file_patterns": ["StorageManagerService\\.java"]
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a9904ba0de91..7276c78b398c 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -60,6 +60,7 @@ import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.DisconnectCause;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
@@ -318,7 +319,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int[] mDataEnabledReason;
- private Map<Integer, Long> mAllowedNetworkTypesList;
+ private int[] mAllowedNetworkTypeReason;
+ private long[] mAllowedNetworkTypeValue;
+
+ private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists;
/**
* Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -350,6 +354,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
}
private boolean isLocationPermissionRequired(Set<Integer> events) {
@@ -383,7 +389,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
|| events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
- || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+ || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
}
private static final int MSG_USER_SWITCHED = 1;
@@ -527,6 +534,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
+ mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
+ mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
// ds -> ss switch.
if (mNumPhones < oldNumPhones) {
@@ -535,6 +544,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
cutListToSize(mPreciseDataConnectionStates, mNumPhones);
cutListToSize(mBarringInfo, mNumPhones);
cutListToSize(mPhysicalChannelConfigs, mNumPhones);
+ cutListToSize(mLinkCapacityEstimateLists, mNumPhones);
return;
}
@@ -571,6 +581,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+ mAllowedNetworkTypeReason[i] = -1;
+ mAllowedNetworkTypeValue[i] = -1;
+ mLinkCapacityEstimateLists.add(i, new ArrayList<>());
}
}
@@ -630,9 +643,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBarringInfo = new ArrayList<>();
mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
mPhysicalChannelConfigs = new ArrayList<>();
- mAllowedNetworkTypesList = new HashMap<>();
+ mAllowedNetworkTypeReason = new int[numPhones];
+ mAllowedNetworkTypeValue = new long[numPhones];
mIsDataEnabled = new boolean[numPhones];
mDataEnabledReason = new int[numPhones];
+ mLinkCapacityEstimateLists = new ArrayList<>();
+
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -665,6 +681,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+ mAllowedNetworkTypeReason[i] = -1;
+ mAllowedNetworkTypeValue[i] = -1;
+ mLinkCapacityEstimateLists.add(i, new ArrayList<>());
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1173,9 +1192,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
+ TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) {
try {
- r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+ if (mLinkCapacityEstimateLists.get(phoneId) != null) {
+ r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists
+ .get(phoneId));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -2454,18 +2476,19 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*
* @param phoneId the phone id.
* @param subId the subId.
- * @param allowedNetworkTypesList Map associating all allowed network type reasons with reason's
- * allowed network type values.
+ * @param reason the allowed network type reason.
+ * @param allowedNetworkType the allowed network type value.
*/
- public void notifyAllowedNetworkTypesChanged(int phoneId, int subId,
- Map allowedNetworkTypesList) {
+ public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, int reason,
+ long allowedNetworkType) {
if (!checkNotifyPermission("notifyAllowedNetworkTypesChanged()")) {
return;
}
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
- mAllowedNetworkTypesList = allowedNetworkTypesList;
+ mAllowedNetworkTypeReason[phoneId] = reason;
+ mAllowedNetworkTypeValue[phoneId] = allowedNetworkType;
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
@@ -2473,10 +2496,48 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
&& idMatch(r.subId, subId, phoneId)) {
try {
if (VDBG) {
- log("notifyAllowedNetworkTypesChanged: AllowedNetworkTypesList= "
- + mAllowedNetworkTypesList.toString());
+ log("notifyAllowedNetworkTypesChanged: reason= " + reason
+ + ", allowed network type:"
+ + TelephonyManager.convertNetworkTypeBitmaskToString(
+ allowedNetworkType));
}
- r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+ r.callback.onAllowedNetworkTypesChanged(reason, allowedNetworkType);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ /**
+ * Notify that the link capacity estimate has changed.
+ * @param phoneId the phone id.
+ * @param subId the subscription id.
+ * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate}
+ */
+ public void notifyLinkCapacityEstimateChanged(int phoneId, int subId,
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ if (!checkNotifyPermission("notifyLinkCapacityEstimateChanged()")) {
+ return;
+ }
+
+ if (VDBG) {
+ log("notifyLinkCapacityEstimateChanged: linkCapacityEstimateList ="
+ + linkCapacityEstimateList);
+ }
+
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mLinkCapacityEstimateLists.set(phoneId, linkCapacityEstimateList);
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2531,6 +2592,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("mDataEnabledReason=" + mDataEnabledReason);
+ pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
+ pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
+ pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
pw.decreaseIndent();
}
pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index ee610671ff23..f5662772f59f 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -90,7 +90,12 @@ class TestNetworkService extends ITestNetworkManager.Stub {
mCm = mContext.getSystemService(ConnectivityManager.class);
mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
TEST_NETWORK_PROVIDER_NAME);
- mCm.registerNetworkProvider(mNetworkProvider);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCm.registerNetworkProvider(mNetworkProvider);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index cd3892da43e4..051cd9907bee 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -64,7 +64,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -666,6 +666,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
requireNonNull(listener, "listener was null");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_FACTORY,
+ "Must have permission NETWORK_FACTORY to unregister a policy listener");
+
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
PolicyListenerBinderDeath listenerBinderDeath =
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 73755231c3be..c360190d58a3 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1076,7 +1076,7 @@ public class AccountManagerService
} catch (RuntimeException e) {
// The account manager only throws security exceptions, so let's
// log all others.
- if (!(e instanceof SecurityException)) {
+ if (!(e instanceof SecurityException || e instanceof IllegalArgumentException)) {
Slog.wtf(TAG, "Account Manager Crash", e);
}
throw e;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f768db1d0821..3c445ae4b667 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -20,11 +20,11 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
+import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER;
import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE;
import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
@@ -75,7 +75,6 @@ import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
@@ -1217,7 +1216,18 @@ public final class ActiveServices {
void killMisbehavingService(ServiceRecord r,
int appUid, int appPid, String localPackageName) {
synchronized (mAm) {
- stopServiceLocked(r, false);
+ if (!r.destroying) {
+ // This service is still alive, stop it.
+ stopServiceLocked(r, false);
+ } else {
+ // Check if there is another instance of it being started in parallel,
+ // if so, stop that too to avoid spamming the system.
+ final ServiceMap smap = getServiceMapLocked(r.userId);
+ final ServiceRecord found = smap.mServicesByInstanceName.remove(r.instanceName);
+ if (found != null) {
+ stopServiceLocked(found, false);
+ }
+ }
mAm.crashApplication(appUid, appPid, localPackageName, -1,
"Bad notification for startForeground", true /*force*/);
}
@@ -1832,7 +1842,7 @@ public final class ActiveServices {
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ SystemClock.uptimeMillis());
}
}
if (alreadyStartedOp) {
@@ -1854,7 +1864,7 @@ public final class ActiveServices {
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ SystemClock.uptimeMillis());
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3756,6 +3766,7 @@ public final class ActiveServices {
}
}
+ final long now = SystemClock.uptimeMillis();
// Check to see if the service had been started as foreground, but being
// brought down before actually showing a notification. That is not allowed.
if (r.fgRequired) {
@@ -3765,8 +3776,7 @@ public final class ActiveServices {
r.fgWaiting = false;
ServiceState stracker = r.getTracker();
if (stracker != null) {
- stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
}
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
@@ -3825,8 +3835,7 @@ public final class ActiveServices {
decActiveForegroundAppLocked(smap, r);
ServiceState stracker = r.getTracker();
if (stracker != null) {
- stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3893,7 +3902,6 @@ public final class ActiveServices {
}
int memFactor = mAm.mProcessStats.getMemFactorLocked();
- long now = SystemClock.uptimeMillis();
if (r.tracker != null) {
r.tracker.setStarted(false, memFactor, now);
r.tracker.setBound(false, memFactor, now);
@@ -5402,16 +5410,28 @@ public final class ActiveServices {
}
}
+ boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
+ if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
+ return true;
+ }
+ final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
+ callingPackage, callingPid, callingUid, null /* serviceRecord */,
+ false /* allowBackgroundActivityStarts */);
+ final @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundLocked(
+ allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */);
+ return allowStartFgs != REASON_DENIED;
+ }
+
/**
* Should allow while-in-use permissions in FGS or not.
* A typical BG started FGS is not allowed to have while-in-use permissions.
* @param callingPackage caller app's package name.
* @param callingUid caller app's uid.
- * @param r the service to start.
+ * @param targetService the service to start.
* @return {@link ReasonCode}
*/
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
- int callingPid, int callingUid, ServiceRecord r,
+ int callingPid, int callingUid, @Nullable ServiceRecord targetService,
boolean allowBackgroundActivityStarts) {
int ret = REASON_DENIED;
@@ -5473,8 +5493,8 @@ public final class ActiveServices {
}
if (ret == REASON_DENIED) {
- if (r.app != null) {
- ActiveInstrumentation instr = r.app.getActiveInstrumentation();
+ if (targetService != null && targetService.app != null) {
+ ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
}
@@ -5520,16 +5540,44 @@ public final class ActiveServices {
private @ReasonCode int shouldAllowFgsStartForegroundLocked(
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
- int ret = allowWhileInUse;
FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason =
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
+ int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid,
+ callingPackage, r);
- final StringBuilder sb = new StringBuilder(64);
final int uidState = mAm.getUidStateLocked(callingUid);
+ final String debugInfo =
+ "[callingPackage: " + callingPackage
+ + "; callingUid: " + callingUid
+ + "; uidState: " + ProcessList.makeProcStateString(uidState)
+ + "; intent: " + intent
+ + "; code:" + reasonCodeToString(ret)
+ + "; tempAllowListReason:<"
+ + (tempAllowListReason == null ? null :
+ (tempAllowListReason.mReason
+ + ",reasonCode:"
+ + reasonCodeToString(tempAllowListReason.mReasonCode)
+ + ",duration:" + tempAllowListReason.mDuration
+ + ",callingUid:" + tempAllowListReason.mCallingUid))
+ + ">"
+ + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
+ + "]";
+ if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
+ r.mLoggedInfoAllowStartForeground = false;
+ r.mInfoAllowStartForeground = debugInfo;
+ }
+ return ret;
+ }
+
+ private @ReasonCode int shouldAllowFgsStartForegroundLocked(@ReasonCode int allowWhileInUse,
+ int callingPid, int callingUid, String callingPackage,
+ @Nullable ServiceRecord targetService) {
+ int ret = allowWhileInUse;
+
if (ret == REASON_DENIED) {
+ final int uidState = mAm.getUidStateLocked(callingUid);
// Is the calling UID at PROCESS_STATE_TOP or above?
if (uidState <= PROCESS_STATE_TOP) {
- sb.append("uidState=").append(uidState);
ret = getReasonCodeFromProcState(uidState);
}
}
@@ -5550,6 +5598,14 @@ public final class ActiveServices {
&& instr.mHasBackgroundForegroundServiceStartsPermission) {
return REASON_INSTR_BACKGROUND_FGS_PERMISSION;
}
+ final long lastInvisibleTime = app.mState.getLastInvisibleTime();
+ if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE) {
+ final long sinceLastInvisible = SystemClock.elapsedRealtime()
+ - lastInvisibleTime;
+ if (sinceLastInvisible < mAm.mConstants.mFgToBgFgsGraceDuration) {
+ return REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
+ }
+ }
}
}
return null;
@@ -5601,8 +5657,10 @@ public final class ActiveServices {
// NOTE this should always be the last check.
if (ret == REASON_DENIED) {
- if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid)
- || isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
+ if (isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
+ ret = REASON_EXEMPTED_PACKAGE;
+ } else if (targetService != null && isPackageExemptedFromFgsRestriction(
+ targetService.appInfo.packageName, targetService.appInfo.uid)) {
ret = REASON_EXEMPTED_PACKAGE;
}
}
@@ -5615,28 +5673,6 @@ public final class ActiveServices {
}
}
- final String debugInfo =
- "[callingPackage: " + callingPackage
- + "; callingUid: " + callingUid
- + "; uidState: " + ProcessList.makeProcStateString(uidState)
- + "; intent: " + intent
- + "; code:" + reasonCodeToString(ret)
- + "; tempAllowListReason:<" +
- (tempAllowListReason == null ? null :
- (tempAllowListReason.mReason
- + ",reasonCode:"
- + reasonCodeToString(tempAllowListReason.mReasonCode)
- + ",duration:" + tempAllowListReason.mDuration
- + ",callingUid:" + tempAllowListReason.mCallingUid))
- + ">"
- + "; extra:" + sb.toString()
- + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
- + "]";
- if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
- r.mLoggedInfoAllowStartForeground = false;
- r.mInfoAllowStartForeground = debugInfo;
- }
-
return ret;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index ba8f1906b0e1..5859cea2d284 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -95,6 +95,7 @@ final class ActivityManagerConstants extends ContentObserver {
"process_crash_count_reset_interval";
static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit";
static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration";
+ static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -133,6 +134,7 @@ final class ActivityManagerConstants extends ContentObserver {
private static final int DEFAULT_PROCESS_CRASH_COUNT_RESET_INTERVAL = 12 * 60 * 60 * 1000;
private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12;
private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 10 * 1000;
+ private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000;
// Flag stored in the DeviceConfig API.
@@ -388,6 +390,12 @@ final class ActivityManagerConstants extends ContentObserver {
*/
volatile long mBootTimeTempAllowlistDuration = DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION;
+ /**
+ * The grace period in milliseconds to allow a process to start FGS from background after
+ * switching from foreground to background; currently it's only applicable to its activities.
+ */
+ volatile long mFgToBgFgsGraceDuration = DEFAULT_FG_TO_BG_FGS_GRACE_DURATION;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -575,6 +583,9 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION:
updateBootTimeTempAllowListDuration();
break;
+ case KEY_FG_TO_BG_FGS_GRACE_DURATION:
+ updateFgToBgFgsGraceDuration();
+ break;
default:
break;
}
@@ -851,6 +862,13 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION);
}
+ private void updateFgToBgFgsGraceDuration() {
+ mFgToBgFgsGraceDuration = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_FG_TO_BG_FGS_GRACE_DURATION,
+ DEFAULT_FG_TO_BG_FGS_GRACE_DURATION);
+ }
+
private void updateImperceptibleKillExemptions() {
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear();
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
@@ -1051,6 +1069,8 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println(MAX_PHANTOM_PROCESSES);
pw.print(" "); pw.print(KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION); pw.print("=");
pw.println(mBootTimeTempAllowlistDuration);
+ pw.print(" "); pw.print(KEY_FG_TO_BG_FGS_GRACE_DURATION); pw.print("=");
+ pw.println(mFgToBgFgsGraceDuration);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
new file mode 100644
index 000000000000..cd4180e46428
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * Interface for in-process calls into
+ * {@link android.content.Context#ACTIVITY_SERVICE ActivityManager system service}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ActivityManagerLocal {
+ /**
+ * Checks whether an app will be able to start a foreground service or not.
+ *
+ * @param pid The process id belonging to the app to be checked.
+ * @param uid The UID of the app to be checked.
+ * @param packageName The package name of the app to be checked.
+ * @return whether the app will be able to start a foreground service or not.
+ */
+ boolean canStartForegroundService(int pid, int uid, @NonNull String packageName);
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8ea6194a9535..0e8644a6569e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -44,11 +44,11 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
-import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.BLUETOOTH_UID;
@@ -273,6 +273,9 @@ import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalMetrics;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
@@ -342,6 +345,7 @@ import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
import com.android.server.IoThread;
+import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.NetworkManagementInternal;
@@ -2324,6 +2328,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mAppOpsService.publish();
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, mInternal);
+ LocalManagerRegistry.addManager(ActivityManagerLocal.class,
+ (ActivityManagerLocal) mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
mPendingIntentController.onActivityManagerInternalAdded();
mAppProfiler.onActivityManagerInternalAdded();
@@ -7697,18 +7703,32 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
- boolean isPackageLoading = false;
+ boolean isIncremental = false;
+ float loadingProgress = 1;
+ long millisSinceOldestPendingRead = 0;
// Notify package manager service to possibly update package state
if (r != null && r.info != null && r.info.packageName != null) {
+ final String codePath = r.info.getCodePath();
mPackageManagerInt.notifyPackageCrashOrAnr(r.info.packageName);
IncrementalStatesInfo incrementalStatesInfo =
mPackageManagerInt.getIncrementalStatesInfo(r.info.packageName, r.uid,
r.userId);
- isPackageLoading = incrementalStatesInfo.isLoading();
- if (isPackageLoading) {
- // Report in the main log that the package is still loading
- Slog.e(TAG, "App crashed when package " + r.info.packageName + " is "
- + ((int) (incrementalStatesInfo.getProgress() * 100)) + "% loaded.");
+ if (incrementalStatesInfo != null) {
+ loadingProgress = incrementalStatesInfo.getProgress();
+ }
+ isIncremental = IncrementalManager.isIncrementalPath(codePath);
+ if (isIncremental) {
+ // Report in the main log about the incremental package
+ Slog.e(TAG, "App crashed on incremental package " + r.info.packageName
+ + " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
+ final IBinder incrementalService = ServiceManager.getService(
+ Context.INCREMENTAL_SERVICE);
+ if (incrementalService != null) {
+ final IncrementalManager incrementalManager = new IncrementalManager(
+ IIncrementalService.Stub.asInterface(incrementalService));
+ IncrementalMetrics metrics = incrementalManager.getMetrics(codePath);
+ millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead();
+ }
}
}
@@ -7737,7 +7757,7 @@ public class ActivityManagerService extends IActivityManager.Stub
processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
: (r != null) ? r.getProcessClassEnum()
: ServerProtoEnums.ERROR_SOURCE_UNKNOWN,
- isPackageLoading
+ isIncremental, loadingProgress, millisSinceOldestPendingRead
);
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
@@ -15069,7 +15089,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@VisibleForTesting
- public final class LocalService extends ActivityManagerInternal {
+ public final class LocalService extends ActivityManagerInternal
+ implements ActivityManagerLocal {
@Override
public String checkContentProviderAccess(String authority, int userId) {
return mCpHelper.checkContentProviderAccess(authority, userId);
@@ -15991,6 +16012,13 @@ public class ActivityManagerService extends IActivityManager.Stub
public void unregisterAnrController(AnrController controller) {
mActivityTaskManager.unregisterAnrController(controller);
}
+
+ @Override
+ public boolean canStartForegroundService(int pid, int uid, @NonNull String packageName) {
+ synchronized (ActivityManagerService.this) {
+ return mServices.canStartForegroundServiceLocked(pid, uid, packageName);
+ }
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -16119,7 +16147,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- public void waitForBroadcastIdle(PrintWriter pw) {
+ @Override
+ public void waitForBroadcastIdle() {
+ waitForBroadcastIdle(/* printWriter= */ null);
+ }
+
+ public void waitForBroadcastIdle(@Nullable PrintWriter pw) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
while (true) {
boolean idle = true;
@@ -16127,9 +16160,11 @@ public class ActivityManagerService extends IActivityManager.Stub
for (BroadcastQueue queue : mBroadcastQueues) {
if (!queue.isIdle()) {
final String msg = "Waiting for queue " + queue + " to become idle...";
- pw.println(msg);
- pw.println(queue.describeState());
- pw.flush();
+ if (pw != null) {
+ pw.println(msg);
+ pw.println(queue.describeState());
+ pw.flush();
+ }
Slog.v(TAG, msg);
queue.cancelDeferrals();
idle = false;
@@ -16139,8 +16174,10 @@ public class ActivityManagerService extends IActivityManager.Stub
if (idle) {
final String msg = "All broadcast queues are idle!";
- pw.println(msg);
- pw.flush();
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
Slog.v(TAG, msg);
return;
} else {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index e2086b01ec13..e74c936af02d 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -589,9 +589,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
Slog.w(TAG, "exception reading modem stats: " + e.getCause());
}
- final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDelta;
+ final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas;
if (mMeasuredEnergySnapshot == null || futureECRs == null) {
- measuredEnergyDelta = null;
+ measuredEnergyDeltas = null;
} else {
final int voltageMv;
synchronized (mStats) {
@@ -610,7 +610,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
ecrs = null;
}
- measuredEnergyDelta = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv);
+ measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv);
}
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -633,10 +633,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
final long[] cpuClusterChargeUC;
- if (measuredEnergyDelta == null) {
+ if (measuredEnergyDeltas == null) {
cpuClusterChargeUC = null;
} else {
- cpuClusterChargeUC = measuredEnergyDelta.cpuClusterChargeUC;
+ cpuClusterChargeUC = measuredEnergyDeltas.cpuClusterChargeUC;
}
mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC);
}
@@ -650,9 +650,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mStats.updateRpmStatsLocked(elapsedRealtimeUs);
}
- // Inform mStats about each applicable measured energy.
- if (measuredEnergyDelta != null) {
- final long displayChargeUC = measuredEnergyDelta.displayChargeUC;
+ // Inform mStats about each applicable measured energy (unless addressed elsewhere).
+ if (measuredEnergyDeltas != null) {
+ final long displayChargeUC = measuredEnergyDeltas.displayChargeUC;
if (displayChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
// If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState,
@@ -660,19 +660,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
}
// Inform mStats about each applicable custom energy bucket.
- if (measuredEnergyDelta != null
- && measuredEnergyDelta.otherTotalChargeUC != null) {
+ if (measuredEnergyDeltas != null
+ && measuredEnergyDeltas.otherTotalChargeUC != null) {
// Iterate over the custom (EnergyConsumerType.OTHER) ordinals.
- for (int ord = 0; ord < measuredEnergyDelta.otherTotalChargeUC.length; ord++) {
- long totalEnergy = measuredEnergyDelta.otherTotalChargeUC[ord];
- SparseLongArray uidEnergies = measuredEnergyDelta.otherUidChargesUC[ord];
+ for (int ord = 0; ord < measuredEnergyDeltas.otherTotalChargeUC.length; ord++) {
+ long totalEnergy = measuredEnergyDeltas.otherTotalChargeUC[ord];
+ SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidChargesUC[ord];
mStats.updateCustomMeasuredEnergyStatsLocked(ord, totalEnergy, uidEnergies);
}
}
if (bluetoothInfo != null) {
if (bluetoothInfo.isValid()) {
- mStats.updateBluetoothStateLocked(bluetoothInfo, elapsedRealtime, uptime);
+ final long btChargeUC = measuredEnergyDeltas != null
+ ? measuredEnergyDeltas.bluetoothChargeUC
+ : MeasuredEnergySnapshot.UNAVAILABLE;
+ mStats.updateBluetoothStateLocked(bluetoothInfo,
+ btChargeUC, elapsedRealtime, uptime);
} else {
Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
}
@@ -684,10 +688,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if (wifiInfo != null) {
if (wifiInfo.isValid()) {
- // TODO: wifiEnergyDelta = measuredEnergyDelta.consumerTypeEnergyUJ
- // .get(EnergyConsumerType.WIFI, MeasuredEnergySnapshot.UNAVAILABLE)
- mStats.updateWifiState(extractDeltaLocked(wifiInfo)
- /*, TODO: wifiEnergyDelta */, elapsedRealtime, uptime);
+ final long wifiChargeUC = measuredEnergyDeltas != null ?
+ measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+ mStats.updateWifiState(
+ extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
} else {
Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
}
@@ -820,13 +824,19 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
for (int idx = 0; idx < size; idx++) {
final EnergyConsumer consumer = idToConsumer.valueAt(idx);
switch (consumer.type) {
+ case EnergyConsumerType.BLUETOOTH:
+ buckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH] = true;
+ break;
+ case EnergyConsumerType.CPU_CLUSTER:
+ buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+ break;
case EnergyConsumerType.DISPLAY:
buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true;
buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true;
buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER] = true;
break;
- case EnergyConsumerType.CPU_CLUSTER:
- buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+ case EnergyConsumerType.WIFI:
+ buckets[MeasuredEnergyStats.POWER_BUCKET_WIFI] = true;
break;
}
}
@@ -864,13 +874,18 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
final IntArray energyConsumerIds = new IntArray();
+ if ((flags & UPDATE_BT) != 0) {
+ addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.BLUETOOTH);
+ }
if ((flags & UPDATE_CPU) != 0) {
addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER);
}
if ((flags & UPDATE_DISPLAY) != 0) {
addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
}
- // TODO: Wifi, Bluetooth, etc., go here
+ if ((flags & UPDATE_WIFI) != 0) {
+ addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI);
+ }
if (energyConsumerIds.size() == 0) {
return null;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dbfa7f34c6f9..c3f97adbd9c3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
import android.annotation.NonNull;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -32,6 +33,7 @@ import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.os.BatteryManagerInternal;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
@@ -184,6 +186,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
};
+ private BatteryManagerInternal mBatteryManagerInternal;
+
private void populatePowerEntityMaps() {
PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
if (entities == null) {
@@ -370,6 +374,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
Slog.e(TAG, "Could not register PowerStatsInternal");
}
}
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
Watchdog.getInstance().addMonitor(this);
@@ -1923,7 +1928,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
- mStats.updateWifiState(info, elapsedRealtime, uptime);
+ mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
});
}
}
@@ -1941,7 +1946,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
synchronized (mStats) {
- mStats.updateBluetoothStateLocked(info, elapsedRealtime, uptime);
+ mStats.updateBluetoothStateLocked(
+ info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
}
});
}
@@ -2715,4 +2721,44 @@ public final class BatteryStatsService extends IBatteryStats.Stub
});
}
}
+
+ /**
+ * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+ */
+ @Override
+ public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ mBatteryManagerInternal.setChargerAcOnline(online, forceUpdate);
+ }
+
+ /**
+ * Sets battery level, and freezes the battery state.
+ */
+ @Override
+ public void setBatteryLevel(int level, boolean forceUpdate) {
+ mBatteryManagerInternal.setBatteryLevel(level, forceUpdate);
+ }
+
+ /**
+ * Unplugs battery, and freezes the battery state.
+ */
+ @Override
+ public void unplugBattery(boolean forceUpdate) {
+ mBatteryManagerInternal.unplugBattery(forceUpdate);
+ }
+
+ /**
+ * Unfreezes battery state, returning to current hardware values.
+ */
+ @Override
+ public void resetBattery(boolean forceUpdate) {
+ mBatteryManagerInternal.resetBattery(forceUpdate);
+ }
+
+ /**
+ * Suspend charging even if plugged in.
+ */
+ @Override
+ public void suspendBatteryInput() {
+ mBatteryManagerInternal.suspendBatteryInput();
+ }
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 81c4c8605fb4..e79f09665153 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1416,7 +1416,7 @@ public final class BroadcastQueue {
}
}
if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
- r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+ r.requiredPermissions != null && r.requiredPermissions.length > 0) {
for (int i = 0; i < r.requiredPermissions.length; i++) {
String requiredPermission = r.requiredPermissions[i];
try {
@@ -1424,7 +1424,7 @@ public final class BroadcastQueue {
checkPermission(requiredPermission,
info.activityInfo.applicationInfo.packageName,
UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
+ .getUserId(info.activityInfo.applicationInfo.uid));
} catch (RemoteException e) {
perm = PackageManager.PERMISSION_DENIED;
}
@@ -1439,36 +1439,18 @@ public final class BroadcastQueue {
break;
}
int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
- && mService.getAppOpsManager().noteOpNoThrow(appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
- null /* default featureId */,
- "Broadcast delivered to " + info.activityInfo.name)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.permissionToOp(
- requiredPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
+ if (!noteOpForManifestReceiver(appOp, r, info, component)) {
+ skip = true;
+ break;
+ }
}
}
}
- if (!skip && r.appOp != AppOpsManager.OP_NONE
- && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
- null /* default featureId */, "Broadcast delivered to " + info.activityInfo.name)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.opToName(r.appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
+ if (!skip && r.appOp != AppOpsManager.OP_NONE) {
+ if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
+ skip = true;
+ }
}
boolean isSingleton = false;
try {
@@ -1717,6 +1699,40 @@ public final class BroadcastQueue {
mPendingBroadcastRecvIndex = recIdx;
}
+ private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
+ ComponentName component) {
+ if (info.activityInfo.attributionTags == null) {
+ return noteOpForManifestReceiverInner(appOp, r, info, component, null);
+ } else {
+ // Attribution tags provided, noteOp each tag
+ for (String tag : info.activityInfo.attributionTags) {
+ if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
+ ComponentName component, String tag) {
+ if (mService.getAppOpsManager().noteOpNoThrow(appOp,
+ info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName,
+ tag,
+ "Broadcast delivered to " + info.activityInfo.name)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires appop " + AppOpsManager.opToName(appOp)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return false;
+ }
+ return true;
+ }
+
private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
return;
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index 9b2ca136bdfb..4c9ab63a100b 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -41,7 +41,7 @@ public class MeasuredEnergySnapshot {
private static final int MILLIVOLTS_PER_VOLT = 1000;
- public static final long UNAVAILABLE = -1L;
+ public static final long UNAVAILABLE = android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
/** Map of {@link EnergyConsumer#id} to its corresponding {@link EnergyConsumer}. */
private final SparseArray<EnergyConsumer> mEnergyConsumers;
@@ -109,12 +109,18 @@ public class MeasuredEnergySnapshot {
/** Class for returning the relevant data calculated from the measured energy delta */
static class MeasuredEnergyDeltaData {
- /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
- public long displayChargeUC = UNAVAILABLE;
+ /** The chargeUC for {@link EnergyConsumerType#BLUETOOTH}. */
+ public long bluetoothChargeUC = UNAVAILABLE;
/** The chargeUC for {@link EnergyConsumerType#CPU_CLUSTER}s. */
public long[] cpuClusterChargeUC = null;
+ /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
+ public long displayChargeUC = UNAVAILABLE;
+
+ /** The chargeUC for {@link EnergyConsumerType#WIFI}. */
+ public long wifiChargeUC = UNAVAILABLE;
+
/** Map of {@link EnergyConsumerType#OTHER} ordinals to their total chargeUC. */
public @Nullable long[] otherTotalChargeUC = null;
@@ -196,8 +202,8 @@ public class MeasuredEnergySnapshot {
final long deltaChargeUC = calculateChargeConsumedUC(deltaUJ, avgVoltageMV);
switch (type) {
- case EnergyConsumerType.DISPLAY:
- output.displayChargeUC = deltaChargeUC;
+ case EnergyConsumerType.BLUETOOTH:
+ output.bluetoothChargeUC = deltaChargeUC;
break;
case EnergyConsumerType.CPU_CLUSTER:
@@ -207,6 +213,14 @@ public class MeasuredEnergySnapshot {
output.cpuClusterChargeUC[ordinal] = deltaChargeUC;
break;
+ case EnergyConsumerType.DISPLAY:
+ output.displayChargeUC = deltaChargeUC;
+ break;
+
+ case EnergyConsumerType.WIFI:
+ output.wifiChargeUC = deltaChargeUC;
+ break;
+
case EnergyConsumerType.OTHER:
if (output.otherTotalChargeUC == null) {
output.otherTotalChargeUC = new long[getNumOtherOrdinals()];
@@ -215,6 +229,7 @@ public class MeasuredEnergySnapshot {
output.otherTotalChargeUC[ordinal] = deltaChargeUC;
output.otherUidChargesUC[ordinal] = otherUidCharges;
break;
+
default:
Slog.w(TAG, "Ignoring consumer " + consumer.name + " of unknown type " + type);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 939d35fdb5ef..5ae65ef0e4be 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1367,6 +1367,7 @@ public final class OomAdjuster {
ProcessRecord app;
int adj;
boolean foregroundActivities;
+ boolean mHasVisibleActivities;
int procState;
int schedGroup;
int appUid;
@@ -1375,10 +1376,12 @@ public final class OomAdjuster {
ProcessStateRecord mState;
void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
- int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
+ boolean hasVisibleActivities, int procState, int schedGroup, int appUid,
+ int logUid, int processStateCurTop) {
this.app = app;
this.adj = adj;
this.foregroundActivities = foregroundActivities;
+ this.mHasVisibleActivities = hasVisibleActivities;
this.procState = procState;
this.schedGroup = schedGroup;
this.appUid = appUid;
@@ -1411,6 +1414,7 @@ public final class OomAdjuster {
mState.setCached(false);
mState.setEmpty(false);
foregroundActivities = true;
+ mHasVisibleActivities = true;
}
@Override
@@ -1436,6 +1440,7 @@ public final class OomAdjuster {
mState.setCached(false);
mState.setEmpty(false);
foregroundActivities = true;
+ mHasVisibleActivities = false;
}
@Override
@@ -1468,6 +1473,7 @@ public final class OomAdjuster {
mState.setCached(false);
mState.setEmpty(false);
foregroundActivities = true;
+ mHasVisibleActivities = false;
}
@Override
@@ -1480,6 +1486,7 @@ public final class OomAdjuster {
"Raise procstate to cached activity: " + app);
}
}
+ mHasVisibleActivities = false;
}
}
@@ -1591,12 +1598,14 @@ public final class OomAdjuster {
int capability = 0;
boolean foregroundActivities = false;
+ boolean hasVisibleActivities = false;
if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
state.setAdjType("top-activity");
foregroundActivities = true;
+ hasVisibleActivities = true;
procState = PROCESS_STATE_CUR_TOP;
state.bumpAllowStartFgsState(PROCESS_STATE_TOP);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1672,11 +1681,12 @@ public final class OomAdjuster {
// Examine all activities if not already foreground.
if (!foregroundActivities && state.getCachedHasActivities()) {
state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
- adj, foregroundActivities, procState, schedGroup, appUid, logUid,
- PROCESS_STATE_CUR_TOP);
+ adj, foregroundActivities, hasVisibleActivities, procState, schedGroup,
+ appUid, logUid, PROCESS_STATE_CUR_TOP);
adj = state.getCachedAdj();
foregroundActivities = state.getCachedForegroundActivities();
+ hasVisibleActivities = state.getCachedHasVisibleActivities();
procState = state.getCachedProcState();
schedGroup = state.getCachedSchedGroup();
}
@@ -2450,6 +2460,7 @@ public final class OomAdjuster {
state.setCurrentSchedulingGroup(schedGroup);
state.setCurProcState(procState);
state.setCurRawProcState(procState);
+ state.updateLastInvisibleTime(hasVisibleActivities);
state.setHasForegroundActivities(foregroundActivities);
state.setCompletedAdjSeq(mAdjSeq);
state.setAllowStartFgs();
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index d03a47afed8a..93f30cc8ac5e 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -26,12 +26,18 @@ import android.app.AnrController;
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManagerInternal;
+import android.os.IBinder;
import android.os.Message;
import android.os.Process;
+import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalMetrics;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -294,14 +300,31 @@ class ProcessErrorStateRecord {
}
// Check if package is still being loaded
- boolean isPackageLoading = false;
+ boolean isIncremental = false;
+ float loadingProgress = 1;
+ long millisSinceOldestPendingRead = 0;
final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
if (aInfo != null && aInfo.packageName != null) {
IncrementalStatesInfo incrementalStatesInfo =
packageManagerInternal.getIncrementalStatesInfo(
aInfo.packageName, mApp.uid, mApp.userId);
if (incrementalStatesInfo != null) {
- isPackageLoading = incrementalStatesInfo.isLoading();
+ loadingProgress = incrementalStatesInfo.getProgress();
+ }
+ final String codePath = aInfo.getCodePath();
+ isIncremental = IncrementalManager.isIncrementalPath(codePath);
+ if (isIncremental) {
+ // Report in the main log that the incremental package is still loading
+ Slog.e(TAG, "App crashed on incremental package " + aInfo.packageName
+ + " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
+ final IBinder incrementalService = ServiceManager.getService(
+ Context.INCREMENTAL_SERVICE);
+ if (incrementalService != null) {
+ final IncrementalManager incrementalManager = new IncrementalManager(
+ IIncrementalService.Stub.asInterface(incrementalService));
+ IncrementalMetrics metrics = incrementalManager.getMetrics(codePath);
+ millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead();
+ }
}
}
@@ -322,10 +345,8 @@ class ProcessErrorStateRecord {
info.append("Parent: ").append(parentShortComponentName).append("\n");
}
- if (isPackageLoading) {
- // Report in the main log that the package is still loading
- final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
- aInfo.packageName, mApp.uid, mApp.userId).getProgress();
+ if (isIncremental) {
+ // Report in the main log about the incremental package
info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
}
@@ -412,7 +433,8 @@ class ProcessErrorStateRecord {
? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
: FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
mApp.getProcessClassEnum(),
- (mApp.info != null) ? mApp.info.packageName : "", isPackageLoading);
+ (mApp.info != null) ? mApp.info.packageName : "",
+ isIncremental, loadingProgress, millisSinceOldestPendingRead);
final ProcessRecord parentPr = parentProcess != null
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 38330fe770fb..4972aa710497 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -786,7 +786,7 @@ public final class ProcessList {
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mAppDataIsolationAllowlistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
@@ -2315,11 +2315,12 @@ public final class ProcessList {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
if (needsStorageDataIsolation(storageManagerInternal, app)) {
- bindMountAppStorageDirs = true;
- if (pkgDataInfoMap == null ||
- !storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
- app.processName)) {
- // Cannot prepare Android/app and Android/obb directory or inode == 0,
+ // We will run prepareStorageDirs() after we trigger zygote fork, so it won't
+ // slow down app starting speed as those dirs might not be cached.
+ if (pkgDataInfoMap != null && storageManagerInternal.isFuseMounted(userId)) {
+ bindMountAppStorageDirs = true;
+ } else {
+ // Fuse is not mounted or inode == 0,
// so we won't mount it in zygote, but resume the mount after unlocking device.
app.setBindMountPending(true);
bindMountAppStorageDirs = false;
@@ -2367,6 +2368,13 @@ public final class ProcessList {
allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
}
+ // This runs after Process.start() as this method may block app process starting time
+ // if dir is not cached. Running this method after Process.start() can make it
+ // cache the dir asynchronously, so zygote can use it without waiting for it.
+ if (bindMountAppStorageDirs) {
+ storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+ app.processName);
+ }
checkSlow(startTime, "startProcess: returned from zygote!");
return startResult;
} finally {
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 6d783fc63901..d97d343a1960 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -43,6 +43,7 @@ import static android.os.Process.SYSTEM_UID;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ProcessRecord.TAG;
+import android.annotation.ElapsedRealtimeLong;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.os.Binder;
@@ -386,6 +387,16 @@ final class ProcessStateRecord {
@GuardedBy("mService")
private boolean mReachable;
+ /**
+ * The most recent time when the last visible activity within this process became invisible.
+ *
+ * <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is
+ * any visible activities within this process at this moment.</p>
+ */
+ @GuardedBy("mService")
+ @ElapsedRealtimeLong
+ private long mLastInvisibleTime;
+
// Below are the cached task info for OomAdjuster only
private static final int VALUE_INVALID = -1;
private static final int VALUE_FALSE = 0;
@@ -1040,18 +1051,19 @@ final class ProcessStateRecord {
@GuardedBy("mService")
void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
- int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid,
- int logUid, int processCurTop) {
+ int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState,
+ int schedGroup, int appUid, int logUid, int processCurTop) {
if (mCachedAdj != ProcessList.INVALID_ADJ) {
return;
}
- callback.initialize(mApp, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
- processCurTop);
+ callback.initialize(mApp, adj, foregroundActivities, hasVisibleActivities, procState,
+ schedGroup, appUid, logUid, processCurTop);
final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
mApp.getWindowProcessController().computeOomAdjFromActivities(callback));
mCachedAdj = callback.adj;
mCachedForegroundActivities = callback.foregroundActivities;
+ mCachedHasVisibleActivities = callback.mHasVisibleActivities ? VALUE_TRUE : VALUE_FALSE;
mCachedProcState = callback.procState;
mCachedSchedGroup = callback.schedGroup;
@@ -1263,6 +1275,21 @@ final class ProcessStateRecord {
return mAllowStartFgs;
}
+ @GuardedBy("mService")
+ void updateLastInvisibleTime(boolean hasVisibleActivities) {
+ if (hasVisibleActivities) {
+ mLastInvisibleTime = Long.MAX_VALUE;
+ } else if (mLastInvisibleTime == Long.MAX_VALUE) {
+ mLastInvisibleTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ @GuardedBy("mService")
+ @ElapsedRealtimeLong
+ long getLastInvisibleTime() {
+ return mLastInvisibleTime;
+ }
+
@GuardedBy({"mService", "mProcLock"})
void dump(PrintWriter pw, String prefix, long nowUptime) {
if (mReportedInteraction || mFgInteractionTime != 0) {
@@ -1340,6 +1367,15 @@ final class ProcessStateRecord {
TimeUtils.formatDuration(mLastTopTime, nowUptime, pw);
pw.println();
}
+ if (mLastInvisibleTime > 0 && mLastInvisibleTime < Long.MAX_VALUE) {
+ pw.print(prefix); pw.print("lastInvisibleTime=");
+ final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
+ final long currentTimeNow = System.currentTimeMillis();
+ final long lastInvisibleCurrentTime =
+ currentTimeNow - elapsedRealtimeNow + mLastInvisibleTime;
+ TimeUtils.dumpTimeWithDelta(pw, lastInvisibleCurrentTime, currentTimeNow);
+ pw.println();
+ }
if (mHasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(mHasStartedServices);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index caf2510e5b1c..ec2020f94969 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -122,7 +122,6 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
@@ -518,18 +517,12 @@ class UserController implements Handler.Callback {
if (!mInjector.getUserManager().isPreCreated(userId)) {
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
userId, 0));
- Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_OFFLOAD
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
- new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE,
- getTemporaryAppAllowlistBroadcastOptions(REASON_LOCKED_BOOT_COMPLETED)
- .toBundle(), true,
- false, MY_PID, SYSTEM_UID,
- Binder.getCallingUid(), Binder.getCallingPid(), userId);
+ // In case of headless system user mode, do not send boot complete broadcast for
+ // system user as it is sent by sendBootCompleted call.
+ if (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
+ // ACTION_LOCKED_BOOT_COMPLETED
+ sendLockedBootCompletedBroadcast(resultTo, userId);
+ }
}
}
@@ -552,6 +545,21 @@ class UserController implements Handler.Callback {
}
}
+ private void sendLockedBootCompletedBroadcast(IIntentReceiver receiver, @UserIdInt int userId) {
+ final Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_OFFLOAD
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null, receiver, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE,
+ getTemporaryAppAllowlistBroadcastOptions(REASON_LOCKED_BOOT_COMPLETED)
+ .toBundle(), true,
+ false, MY_PID, SYSTEM_UID,
+ Binder.getCallingUid(), Binder.getCallingPid(), userId);
+ }
+
/**
* Step from {@link UserState#STATE_RUNNING_LOCKED} to
* {@link UserState#STATE_RUNNING_UNLOCKING}.
@@ -2167,26 +2175,22 @@ class UserController implements Handler.Callback {
}
void sendBootCompleted(IIntentReceiver resultTo) {
- final boolean systemUserFinishedBooting;
-
// Get a copy of mStartedUsers to use outside of lock
SparseArray<UserState> startedUsers;
synchronized (mLock) {
- systemUserFinishedBooting = mCurrentUserId != UserHandle.USER_SYSTEM;
startedUsers = mStartedUsers.clone();
}
for (int i = 0; i < startedUsers.size(); i++) {
UserState uss = startedUsers.valueAt(i);
- if (systemUserFinishedBooting && uss.mHandle.isSystem()) {
- // On Automotive, at this point the system user has already been started and
- // unlocked, and some of the tasks we do here have already been done. So skip those
- // in that case.
- // TODO(b/132262830): this workdound shouldn't be necessary once we move the
- // headless-user start logic to UserManager-land
- Slog.d(TAG, "sendBootCompleted(): skipping on non-current system user");
- continue;
+ if (!UserManager.isHeadlessSystemUserMode()) {
+ finishUserBoot(uss, resultTo);
+ } else if (uss.mHandle.isSystem()) {
+ // In case of headless system user mode, send only locked boot complete broadcast
+ // for system user since finishUserBoot call will be made using other code path;
+ // for non-system user, do nothing since finishUserBoot will be called elsewhere.
+ sendLockedBootCompletedBroadcast(resultTo, uss.mHandle.getIdentifier());
+ return;
}
- finishUserBoot(uss, resultTo);
}
}
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 3e32380b60a9..2982545a5e6f 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -137,6 +137,11 @@ public class GameManagerSettings {
boolean readPersistentDataLocked() {
mGameModes.clear();
+ if (!mSettingsFile.exists()) {
+ Slog.v(GameManagerService.TAG, "Settings file doesn't exists, skip reading");
+ return false;
+ }
+
try {
final FileInputStream str = mSettingsFile.openRead();
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6614e06aba8c..109ffe38f332 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2098,26 +2098,28 @@ public class AppOpsService extends IAppOpsService.Stub {
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
-
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
- boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
- boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
- boolean isCallerPermissionController;
- try {
- isCallerPermissionController = pm.getPackageUid(
- mContext.getPackageManager().getPermissionControllerPackageName(), 0)
- == Binder.getCallingUid();
- } catch (PackageManager.NameNotFoundException doesNotHappen) {
- return;
- }
+ boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
+ if (!isSelfRequest) {
+ boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
+ boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+ boolean isCallerPermissionController;
+ try {
+ isCallerPermissionController = pm.getPackageUid(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ == Binder.getCallingUid();
+ } catch (PackageManager.NameNotFoundException doesNotHappen) {
+ return;
+ }
- if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
- mHandler.post(() -> callback.sendResult(new Bundle()));
- return;
- }
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
+ mHandler.post(() -> callback.sendResult(new Bundle()));
+ return;
+ }
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+ }
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
@@ -3089,14 +3091,10 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED;
}
- // This is a workaround for R QPR, new API change is not allowed. We only allow the current
- // voice recognizer is also the voice interactor to noteproxy op.
- final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext,
- proxyPackageName, code, UserHandle.getUserId(proxyUid));
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame;
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
@@ -3574,14 +3572,10 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED;
}
- // This is a workaround for R QPR, new API change is not allowed. We only allow the current
- // voice recognizer is also the voice interactor to noteproxy op.
- final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext,
- proxyPackageName, code, UserHandle.getUserId(proxyUid));
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame;
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index ed62abc7d773..2b0157c88136 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -49,8 +49,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
-import libcore.util.EmptyArray;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -85,6 +83,8 @@ final class DiscreteRegistry {
private static final String TAG = DiscreteRegistry.class.getSimpleName();
private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
+ private static final long TIMELINE_QUANTIZATION = Duration.ofMinutes(1).toMillis();
+
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
private static final int CURRENT_VERSION = 1;
@@ -107,6 +107,8 @@ final class DiscreteRegistry {
private static final String ATTR_UID_STATE = "us";
private static final String ATTR_FLAGS = "f";
+ private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+
// Lock for read/write access to on disk state
private final Object mOnDiskLock = new Object();
@@ -119,6 +121,9 @@ final class DiscreteRegistry {
@GuardedBy("mInMemoryLock")
private DiscreteOps mDiscreteOps;
+ @GuardedBy("mOnDiskLock")
+ private DiscreteOps mCachedOps = null;
+
DiscreteRegistry(Object inMemoryLock) {
mInMemoryLock = inMemoryLock;
mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
@@ -173,23 +178,25 @@ final class DiscreteRegistry {
}
}
}
- }
- DiscreteOps discreteOps;
- synchronized (mInMemoryLock) {
- discreteOps = mDiscreteOps;
- mDiscreteOps = new DiscreteOps();
- }
- if (discreteOps.isEmpty()) {
- return;
- }
- long currentTimeStamp = Instant.now().toEpochMilli();
- try {
- final File file = new File(mDiscreteAccessDir, currentTimeStamp + TIMELINE_FILE_SUFFIX);
- discreteOps.writeToFile(file);
- } catch (Throwable t) {
- Slog.e(TAG,
- "Error writing timeline state: " + t.getMessage() + " "
- + Arrays.toString(t.getStackTrace()));
+ DiscreteOps discreteOps;
+ synchronized (mInMemoryLock) {
+ discreteOps = mDiscreteOps;
+ mDiscreteOps = new DiscreteOps();
+ mCachedOps = null;
+ }
+ if (discreteOps.isEmpty()) {
+ return;
+ }
+ long currentTimeStamp = Instant.now().toEpochMilli();
+ try {
+ final File file = new File(mDiscreteAccessDir,
+ currentTimeStamp + TIMELINE_FILE_SUFFIX);
+ discreteOps.writeToFile(file);
+ } catch (Throwable t) {
+ Slog.e(TAG,
+ "Error writing timeline state: " + t.getMessage() + " "
+ + Arrays.toString(t.getStackTrace()));
+ }
}
}
@@ -197,25 +204,33 @@ final class DiscreteRegistry {
long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
@Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
- writeAndClearAccessHistory();
- DiscreteOps discreteOps = new DiscreteOps();
- readDiscreteOpsFromDisk(discreteOps, beginTimeMillis, endTimeMillis, filter, uidFilter,
- packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+ DiscreteOps discreteOps = getAndCacheDiscreteOps();
+ discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
+ opNamesFilter, attributionTagFilter, flagsFilter);
discreteOps.applyToHistoricalOps(result);
return;
}
- private void readDiscreteOpsFromDisk(DiscreteOps discreteOps, long beginTimeMillis,
- long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
- @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ private DiscreteOps getAndCacheDiscreteOps() {
+ DiscreteOps discreteOps = new DiscreteOps();
+
synchronized (mOnDiskLock) {
- long historyBeginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
- ChronoUnit.MILLIS).toEpochMilli();
- if (historyBeginTimeMillis > endTimeMillis) {
- return;
+ synchronized (mInMemoryLock) {
+ discreteOps.merge(mDiscreteOps);
+ }
+ if (mCachedOps == null) {
+ mCachedOps = new DiscreteOps();
+ readDiscreteOpsFromDisk(mCachedOps);
}
- beginTimeMillis = max(beginTimeMillis, historyBeginTimeMillis);
+ discreteOps.merge(mCachedOps);
+ }
+ return discreteOps;
+ }
+
+ private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
+ synchronized (mOnDiskLock) {
+ long beginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+ ChronoUnit.MILLIS).toEpochMilli();
final File[] files = mDiscreteAccessDir.listFiles();
if (files != null && files.length > 0) {
@@ -229,8 +244,7 @@ final class DiscreteRegistry {
if (timestamp < beginTimeMillis) {
continue;
}
- discreteOps.readFromFile(f, beginTimeMillis, endTimeMillis, filter, uidFilter,
- packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+ discreteOps.readFromFile(f, beginTimeMillis);
}
}
}
@@ -251,15 +265,11 @@ final class DiscreteRegistry {
@AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
@NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
int nDiscreteOps) {
- DiscreteOps discreteOps = new DiscreteOps();
- synchronized (mOnDiskLock) {
- writeAndClearAccessHistory();
- String[] opNamesFilter = dumpOp == OP_NONE ? EmptyArray.STRING
- : new String[]{AppOpsManager.opToPublicName(dumpOp)};
- readDiscreteOpsFromDisk(discreteOps, 0, Instant.now().toEpochMilli(), filter,
- uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
- OP_FLAGS_ALL);
- }
+ DiscreteOps discreteOps = getAndCacheDiscreteOps();
+ String[] opNamesFilter = dumpOp == OP_NONE ? null
+ : new String[]{AppOpsManager.opToPublicName(dumpOp)};
+ discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
+ opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
}
@@ -270,7 +280,7 @@ final class DiscreteRegistry {
if (!isDiscreteUid(uid)) {
return false;
}
- if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) == 0) {
+ if ((flags & (OP_FLAGS_DISCRETE)) == 0) {
return false;
}
return true;
@@ -298,6 +308,19 @@ final class DiscreteRegistry {
mUids = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mUids.isEmpty();
+ }
+
+ void merge(DiscreteOps other) {
+ int nUids = other.mUids.size();
+ for (int i = 0; i < nUids; i++) {
+ int uid = other.mUids.keyAt(i);
+ DiscreteUidOps uidOps = other.mUids.valueAt(i);
+ getOrCreateDiscreteUidOps(uid).merge(uidOps);
+ }
+ }
+
void addDiscreteAccess(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
@@ -305,6 +328,25 @@ final class DiscreteRegistry {
uidState, accessTime, accessDuration);
}
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ if ((filter & FILTER_BY_UID) != 0) {
+ ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
+ uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
+ mUids = uids;
+ }
+ int nUids = mUids.size();
+ for (int i = nUids - 1; i >= 0; i--) {
+ mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
+ opNamesFilter, attributionTagFilter, flagsFilter);
+ if (mUids.valueAt(i).isEmpty()) {
+ mUids.removeAt(i);
+ }
+ }
+ }
+
private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
@@ -353,14 +395,7 @@ final class DiscreteRegistry {
return result;
}
- boolean isEmpty() {
- return mUids.isEmpty();
- }
-
- private void readFromFile(File f, long beginTimeMillis, long endTimeMillis,
- @AppOpsManager.HistoricalOpsRequestFilter int filter,
- int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ private void readFromFile(File f, long beginTimeMillis) {
try {
FileInputStream stream = new FileInputStream(f);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -377,12 +412,7 @@ final class DiscreteRegistry {
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_UID.equals(parser.getName())) {
int uid = parser.getAttributeInt(null, ATTR_UID, -1);
- if ((filter & FILTER_BY_UID) != 0 && uid != uidFilter) {
- continue;
- }
- getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis,
- endTimeMillis, filter, packageNameFilter, opNamesFilter,
- attributionTagFilter, flagsFilter);
+ getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis);
}
}
} catch (Throwable t) {
@@ -400,6 +430,38 @@ final class DiscreteRegistry {
mPackages = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mPackages.isEmpty();
+ }
+
+ void merge(DiscreteUidOps other) {
+ int nPackages = other.mPackages.size();
+ for (int i = 0; i < nPackages; i++) {
+ String packageName = other.mPackages.keyAt(i);
+ DiscretePackageOps p = other.mPackages.valueAt(i);
+ getOrCreateDiscretePackageOps(packageName).merge(p);
+ }
+ }
+
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
+ ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
+ packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
+ mPackages = packages;
+ }
+ int nPackages = mPackages.size();
+ for (int i = nPackages - 1; i >= 0; i--) {
+ mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
+ attributionTagFilter, flagsFilter);
+ if (mPackages.valueAt(i).isEmpty()) {
+ mPackages.removeAt(i);
+ }
+ }
+ }
+
void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
@@ -445,22 +507,12 @@ final class DiscreteRegistry {
}
}
- void deserialize(TypedXmlPullParser parser, long beginTimeMillis,
- long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String packageNameFilter,
- @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_PACKAGE.equals(parser.getName())) {
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
- if ((filter & FILTER_BY_PACKAGE_NAME) != 0
- && !packageName.equals(packageNameFilter)) {
- continue;
- }
- getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis,
- endTimeMillis, filter, opNamesFilter, attributionTagFilter,
- flagsFilter);
+ getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis);
}
}
}
@@ -473,6 +525,10 @@ final class DiscreteRegistry {
mPackageOps = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mPackageOps.isEmpty();
+ }
+
void addDiscreteAccess(int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
@@ -480,6 +536,35 @@ final class DiscreteRegistry {
accessDuration);
}
+ void merge(DiscretePackageOps other) {
+ int nOps = other.mPackageOps.size();
+ for (int i = 0; i < nOps; i++) {
+ int opId = other.mPackageOps.keyAt(i);
+ DiscreteOp op = other.mPackageOps.valueAt(i);
+ getOrCreateDiscreteOp(opId).merge(op);
+ }
+ }
+
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
+ @AppOpsManager.OpFlags int flagsFilter) {
+ int nOps = mPackageOps.size();
+ for (int i = nOps - 1; i >= 0; i--) {
+ int opId = mPackageOps.keyAt(i);
+ if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
+ AppOpsManager.opToPublicName(opId))) {
+ mPackageOps.removeAt(i);
+ continue;
+ }
+ mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
+ attributionTagFilter, flagsFilter);
+ if (mPackageOps.valueAt(i).isEmpty()) {
+ mPackageOps.removeAt(i);
+ }
+ }
+ }
+
private DiscreteOp getOrCreateDiscreteOp(int op) {
DiscreteOp result = mPackageOps.get(op);
if (result == null) {
@@ -519,20 +604,12 @@ final class DiscreteRegistry {
}
}
- void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
- @AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_OP.equals(parser.getName())) {
int op = parser.getAttributeInt(null, ATTR_OP_ID);
- if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
- AppOpsManager.opToPublicName(op))) {
- continue;
- }
- getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis, endTimeMillis,
- filter, attributionTagFilter, flagsFilter);
+ getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis);
}
}
}
@@ -545,31 +622,66 @@ final class DiscreteRegistry {
mAttributedOps = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mAttributedOps.isEmpty();
+ }
+
+ void merge(DiscreteOp other) {
+ int nTags = other.mAttributedOps.size();
+ for (int i = 0; i < nTags; i++) {
+ String tag = other.mAttributedOps.keyAt(i);
+ List<DiscreteOpEvent> otherEvents = other.mAttributedOps.valueAt(i);
+ List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(tag);
+ mAttributedOps.put(tag, stableListMerge(events, otherEvents));
+ }
+ }
+
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
+ ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
+ attributedOps.put(attributionTagFilter,
+ getOrCreateDiscreteOpEventsList(attributionTagFilter));
+ mAttributedOps = attributedOps;
+ }
+
+ int nTags = mAttributedOps.size();
+ for (int i = nTags - 1; i >= 0; i--) {
+ String tag = mAttributedOps.keyAt(i);
+ List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
+ list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+ mAttributedOps.put(tag, list);
+ if (list.size() == 0) {
+ mAttributedOps.removeAt(i);
+ }
+ }
+ }
+
void addDiscreteAccess(@Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
attributionTag);
- accessTime = Instant.ofEpochMilli(accessTime).truncatedTo(
- ChronoUnit.MINUTES).toEpochMilli();
+ accessTime = accessTime / TIMELINE_QUANTIZATION * TIMELINE_QUANTIZATION;
int nAttributedOps = attributedOps.size();
- for (int i = nAttributedOps - 1; i >= 0; i--) {
- DiscreteOpEvent previousOp = attributedOps.get(i);
- if (i == nAttributedOps - 1 && previousOp.mNoteTime == accessTime
- && accessDuration > -1) {
- // existing event with updated duration
- attributedOps.remove(i);
- break;
- }
+ int i = nAttributedOps;
+ for (; i > 0; i--) {
+ DiscreteOpEvent previousOp = attributedOps.get(i - 1);
if (previousOp.mNoteTime < accessTime) {
break;
}
if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
- return;
+ if (accessDuration != previousOp.mNoteDuration
+ && accessDuration > TIMELINE_QUANTIZATION) {
+ break;
+ } else {
+ return;
+ }
}
}
- attributedOps.add(new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+ attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
}
private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -633,18 +745,11 @@ final class DiscreteRegistry {
}
}
- void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
- @AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_TAG.equals(parser.getName())) {
String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
- if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !attributionTag.equals(
- attributionTagFilter)) {
- continue;
- }
List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
attributionTag);
int innerDepth = parser.getDepth();
@@ -655,11 +760,7 @@ final class DiscreteRegistry {
-1);
int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
- if ((flagsFilter & opFlags) == 0) {
- continue;
- }
- if ((noteTime + noteDuration < beginTimeMillis
- && noteTime > endTimeMillis)) {
+ if (noteTime + noteDuration < beginTimeMillis) {
continue;
}
DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
@@ -715,5 +816,41 @@ final class DiscreteRegistry {
out.attributeInt(null, ATTR_FLAGS, mOpFlag);
}
}
+
+ private static List<DiscreteOpEvent> stableListMerge(List<DiscreteOpEvent> a,
+ List<DiscreteOpEvent> b) {
+ int nA = a.size();
+ int nB = b.size();
+ int i = 0;
+ int k = 0;
+ List<DiscreteOpEvent> result = new ArrayList<>(nA + nB);
+ while (i < nA || k < nB) {
+ if (i == nA) {
+ result.add(b.get(k++));
+ } else if (k == nB) {
+ result.add(a.get(i++));
+ } else if (a.get(i).mNoteTime < b.get(k).mNoteTime) {
+ result.add(a.get(i++));
+ } else {
+ result.add(b.get(k++));
+ }
+ }
+ return result;
+ }
+
+ private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
+ long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+ int n = list.size();
+ List<DiscreteOpEvent> result = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ DiscreteOpEvent event = list.get(i);
+ if ((event.mOpFlag & flagsFilter) != 0
+ && event.mNoteTime + event.mNoteDuration > beginTimeMillis
+ && event.mNoteTime < endTimeMillis) {
+ result.add(event);
+ }
+ }
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 22d628b8e789..4435c4795730 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -532,7 +532,7 @@ final class HistoricalRegistry {
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
attributionTag, uidState, flags, increment);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, increment, eventStartTime);
+ flags, uidState, eventStartTime, increment);
}
}
}
@@ -795,7 +795,7 @@ final class HistoricalRegistry {
private static boolean isApiEnabled() {
return Binder.getCallingUid() == Process.myUid()
|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+ PROPERTY_PERMISSIONS_HUB_ENABLED, true);
}
private static final class Persistence {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 68f10a5106ef..0a6847515137 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -61,6 +61,8 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
@@ -520,6 +522,7 @@ public class AudioService extends IAudioService.Stub
/** Interface for UserManagerService. */
private final UserManagerInternal mUserManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
+ private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
private final UserRestrictionsListener mUserRestrictionsListener =
new AudioServiceUserRestrictionsListener();
@@ -720,9 +723,12 @@ public class AudioService extends IAudioService.Stub
private String mEnabledSurroundFormats;
private boolean mSurroundModeChanged;
+ private boolean mSupportsMicPrivacyToggle;
+
private boolean mMicMuteFromSwitch;
private boolean mMicMuteFromApi;
private boolean mMicMuteFromRestrictions;
+ private boolean mMicMuteFromPrivacyToggle;
// caches the value returned by AudioSystem.isMicrophoneMuted()
private boolean mMicMuteFromSystemCached;
@@ -822,6 +828,8 @@ public class AudioService extends IAudioService.Stub
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mSensorPrivacyManagerInternal =
+ LocalServices.getService(SensorPrivacyManagerInternal.class);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
@@ -831,6 +839,9 @@ public class AudioService extends IAudioService.Stub
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
+ mSupportsMicPrivacyToggle = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE);
+
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -1106,6 +1117,16 @@ public class AudioService extends IAudioService.Stub
}
}
+ if (mSupportsMicPrivacyToggle) {
+ mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
+ SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
+ if (userId == getCurrentUserId()) {
+ mMicMuteFromPrivacyToggle = enabled;
+ setMicrophoneMuteNoCallerCheck(getCurrentUserId());
+ }
+ });
+ }
+
mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
sendMsg(mAudioHandler,
@@ -2237,7 +2258,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
- enforceModifyAudioRoutingPermission();
+ enforceQueryStateOrModifyRoutingPermission();
return getDevicesForAttributesInt(attributes);
}
@@ -2879,6 +2900,16 @@ public class AudioService extends IAudioService.Stub
}
}
+ private void enforceQueryStateOrModifyRoutingPermission() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Missing MODIFY_AUDIO_ROUTING or QUERY_AUDIO_STATE permissions");
+ }
+ }
+
/** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
String callingPackage) {
@@ -3840,11 +3871,12 @@ public class AudioService extends IAudioService.Stub
* @return true if microphone is reported as muted by primary HAL
*/
public boolean isMicrophoneMuted() {
- return mMicMuteFromSystemCached;
+ return mMicMuteFromSystemCached && !mMicMuteFromPrivacyToggle;
}
private boolean isMicrophoneSupposedToBeMuted() {
- return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi;
+ return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi
+ || mMicMuteFromPrivacyToggle;
}
private void setMicrophoneMuteNoCallerCheck(int userId) {
@@ -5792,7 +5824,7 @@ public class AudioService extends IAudioService.Stub
public @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify permissions
- enforceModifyAudioRoutingPermission();
+ enforceQueryStateOrModifyRoutingPermission();
// translate Java device type to native device type (for the devices masks for full / fixed)
final int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
@@ -7474,6 +7506,13 @@ public class AudioService extends IAudioService.Stub
// the current audio focus owner is no longer valid
mMediaFocusControl.discardAudioFocusOwner();
+ if (mSupportsMicPrivacyToggle) {
+ mMicMuteFromPrivacyToggle = mSensorPrivacyManagerInternal
+ .isSensorPrivacyEnabled(getCurrentUserId(),
+ SensorPrivacyManager.Sensors.MICROPHONE);
+ setMicrophoneMuteNoCallerCheck(getCurrentUserId());
+ }
+
// load volume settings for new user
readAudioSettings(true /*userSwitch*/);
// preserve STREAM_MUSIC volume from one user to the next.
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 050b28b363d2..5b9fa7922e9d 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -340,6 +340,20 @@ public class AuthService extends SystemService {
}
@Override
+ public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
+ int userId, byte[] hardwareAuthToken) throws RemoteException {
+ checkInternalPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mBiometricService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId,
+ hardwareAuthToken);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public CharSequence getButtonLabel(
int userId,
String opPackageName,
@@ -406,26 +420,49 @@ public class AuthService extends SystemService {
mBiometricService.getCurrentModality(
opPackageName, userId, callingUserId, authenticators);
+ final boolean isCredentialAllowed = Utils.isCredentialRequested(authenticators);
+
final String result;
switch (getCredentialBackupModality(modality)) {
case BiometricAuthenticator.TYPE_NONE:
result = null;
break;
+
case BiometricAuthenticator.TYPE_CREDENTIAL:
result = getContext().getString(
R.string.screen_lock_dialog_default_subtitle);
break;
+
case BiometricAuthenticator.TYPE_FINGERPRINT:
- result = getContext().getString(
- R.string.fingerprint_dialog_default_subtitle);
+ if (isCredentialAllowed) {
+ result = getContext().getString(
+ R.string.fingerprint_or_screen_lock_dialog_default_subtitle);
+ } else {
+ result = getContext().getString(
+ R.string.fingerprint_dialog_default_subtitle);
+ }
break;
+
case BiometricAuthenticator.TYPE_FACE:
- result = getContext().getString(R.string.face_dialog_default_subtitle);
+ if (isCredentialAllowed) {
+ result = getContext().getString(
+ R.string.face_or_screen_lock_dialog_default_subtitle);
+ } else {
+ result = getContext().getString(R.string.face_dialog_default_subtitle);
+ }
break;
+
default:
- result = getContext().getString(R.string.biometric_dialog_default_subtitle);
+ if (isCredentialAllowed) {
+ result = getContext().getString(
+ R.string.biometric_or_screen_lock_dialog_default_subtitle);
+ } else {
+ result = getContext().getString(
+ R.string.biometric_dialog_default_subtitle);
+ }
break;
}
+
return result;
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a88820988ef7..63e7b4b84366 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -754,7 +754,7 @@ public class BiometricService extends SystemService {
}
}
- @Override
+ @Override // Binder call
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) {
checkInternalPermission();
@@ -790,6 +790,45 @@ public class BiometricService extends SystemService {
}
@Override // Binder call
+ public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
+ int userId, byte[] hardwareAuthToken) {
+ checkInternalPermission();
+
+ // Check originating strength
+ if (!Utils.isAtLeastStrength(getSensorForId(fromSensorId).getCurrentStrength(),
+ Authenticators.BIOMETRIC_STRONG)) {
+ Slog.w(TAG, "Sensor: " + fromSensorId + " is does not meet the required strength to"
+ + " request resetLockout");
+ return;
+ }
+
+ // Request resetLockout for applicable sensors
+ for (BiometricSensor sensor : mSensors) {
+ if (sensor.id == fromSensorId) {
+ continue;
+ }
+ try {
+ final SensorPropertiesInternal props = sensor.impl
+ .getSensorProperties(getContext().getOpPackageName());
+ final boolean supportsChallengelessHat =
+ props.resetLockoutRequiresHardwareAuthToken
+ && !props.resetLockoutRequiresChallenge;
+ final boolean doesNotRequireHat = !props.resetLockoutRequiresHardwareAuthToken;
+
+ if (supportsChallengelessHat || doesNotRequireHat) {
+ Slog.d(TAG, "resetLockout from: " + fromSensorId
+ + ", for: " + sensor.id
+ + ", userId: " + userId);
+ sensor.impl.resetLockout(token, opPackageName, userId,
+ hardwareAuthToken);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ @Override // Binder call
public int getCurrentStrength(int sensorId) {
checkInternalPermission();
@@ -1294,6 +1333,16 @@ public class BiometricService extends SystemService {
}
}
+ @Nullable
+ private BiometricSensor getSensorForId(int sensorId) {
+ for (BiometricSensor sensor : mSensors) {
+ if (sensor.id == sensorId) {
+ return sensor;
+ }
+ }
+ return null;
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("Sensors:");
for (BiometricSensor sensor : mSensors) {
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index d9e21a83e45a..f4cb38738f7e 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -176,7 +176,8 @@ public class Utils {
* @param requestedStrength the strength that it must meet
* @return true only if the sensor is at least as strong as the requested strength
*/
- public static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) {
+ public static boolean isAtLeastStrength(@Authenticators.Types int sensorStrength,
+ @Authenticators.Types int requestedStrength) {
// Clear out any bits that are not reserved for biometric
sensorStrength &= Authenticators.BIOMETRIC_MIN_STRENGTH;
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index b31a54b8b15e..9617bb09e153 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
@@ -50,6 +51,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
private final boolean mIsStrongBiometric;
private final boolean mRequireConfirmation;
private final ActivityTaskManager mActivityTaskManager;
+ private final BiometricManager mBiometricManager;
@Nullable private final TaskStackListener mTaskStackListener;
private final LockoutTracker mLockoutTracker;
private final boolean mIsRestricted;
@@ -73,6 +75,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
mActivityTaskManager = ActivityTaskManager.getInstance();
+ mBiometricManager = context.getSystemService(BiometricManager.class);
mTaskStackListener = taskStackListener;
mLockoutTracker = lockoutTracker;
mIsRestricted = restricted;
@@ -207,6 +210,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
for (int i = 0; i < hardwareAuthToken.size(); i++) {
byteToken[i] = hardwareAuthToken.get(i);
}
+
+ if (mIsStrongBiometric) {
+ mBiometricManager.resetLockoutTimeBound(getToken(),
+ getContext().getOpPackageName(),
+ getSensorId(), getTargetUserId(), byteToken);
+ }
+
if (isBiometricPrompt() && listener != null) {
// BiometricService will add the token to keystore
listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 06b049be4501..2926260321f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -104,4 +104,11 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFaceService.getAuthenticatorId(mSensorId, callingUserId);
}
+
+ @Override
+ public void resetLockout(IBinder token, String opPackageName, int userId,
+ byte[] hardwareAuthToken) throws RemoteException {
+ mFaceService.resetLockout(token, mSensorId, userId, hardwareAuthToken,
+ opPackageName);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 6dbd590df851..a74e2da30077 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -393,14 +393,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
final IFaceServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
- if (provider == null) {
- Slog.w(TAG, "Null provider for removeAll");
- return;
+ for (ServiceProvider provider : mServiceProviders) {
+ List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
+ for (FaceSensorPropertiesInternal prop : props) {
+ provider.scheduleRemoveAll(prop.sensorId, token, userId, receiver,
+ opPackageName);
+ }
}
-
- provider.second.scheduleRemoveAll(provider.first, token, userId, receiver,
- opPackageName);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 1d8f210b394e..07a653fcfd3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -137,7 +137,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
prop.commonProps.sensorId, prop.commonProps.sensorStrength,
prop.commonProps.maxEnrollmentsPerUser, false /* supportsFaceDetection */,
- prop.halControlsPreview);
+ prop.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 71bac577a4a0..ce728bc7c315 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -62,6 +62,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<ISession> {
// Nothing to do here
}
+ @Override
public void start(@NonNull Callback callback) {
super.start(callback);
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 3434acbf73cc..c7d2f0f87b6c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -495,6 +495,15 @@ public class Sensor {
Slog.w(mTag, "setTestHalEnabled: " + enabled);
if (enabled != mTestHalEnabled) {
// The framework should retrieve a new session from the HAL.
+ try {
+ if (mCurrentSession != null && mCurrentSession.mSession != null) {
+ // TODO(181984005): This should be scheduled instead of directly invoked
+ Slog.d(mTag, "Closing old session");
+ mCurrentSession.mSession.close(888 /* cookie */);
+ }
+ } catch (RemoteException e) {
+ Slog.e(mTag, "RemoteException", e);
+ }
mCurrentSession = null;
}
mTestHalEnabled = enabled;
@@ -519,6 +528,11 @@ public class Sensor {
proto.end(userToken);
}
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
+ mSensorProperties.resetLockoutRequiresHardwareAuthToken);
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
+ mSensorProperties.resetLockoutRequiresChallenge);
+
proto.end(sensorToken);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index b4c9b290cd06..4ca85d000d19 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -139,8 +139,4 @@ public class TestHal extends IFace.Stub {
}
};
}
-
- @Override
- public void reset() {
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 1b9bd7fd0cea..40c050f4838b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -332,7 +332,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@NonNull BiometricScheduler scheduler) {
mSensorProperties = new FaceSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength),
- maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination);
+ maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination,
+ true /* resetLockoutRequiresChallenge */);
mContext = context;
mSensorId = sensorId;
mScheduler = scheduler;
@@ -797,6 +798,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
proto.end(userToken);
}
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
+ mSensorProperties.resetLockoutRequiresHardwareAuthToken);
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
+ mSensorProperties.resetLockoutRequiresChallenge);
+
proto.end(sensorToken);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
index 4cdb68df70af..84aa6d9ad1f8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
@@ -23,11 +23,11 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.biometrics.face.V1_0.OptionalBool;
import android.hardware.biometrics.face.V1_0.OptionalUint64;
import android.hardware.biometrics.face.V1_0.Status;
-import android.os.NativeHandle;
import android.os.RemoteException;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.Arrays;
public class TestHal extends IBiometricsFace.Stub {
private static final String TAG = "face.hidl.TestHal";
@@ -107,8 +107,12 @@ public class TestHal extends IBiometricsFace.Stub {
}
@Override
- public int remove(int faceId) {
+ public int remove(int faceId) throws RemoteException {
Slog.w(TAG, "remove");
+ if (mCallback != null) {
+ mCallback.onRemoved(0 /* deviceId */, new ArrayList<Integer>(Arrays.asList(faceId)),
+ 0 /* userId */);
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 32e9409de4b2..9e82ffcfadc6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -105,4 +105,11 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFingerprintService.getAuthenticatorId(mSensorId, callingUserId);
}
+
+ @Override
+ public void resetLockout(IBinder token, String opPackageName, int userId,
+ byte[] hardwareAuthToken) throws RemoteException {
+ mFingerprintService.resetLockout(token, mSensorId, userId, hardwareAuthToken,
+ opPackageName);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 396dd5f42d4d..3f489e98d5f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -506,13 +506,13 @@ public class FingerprintService extends SystemService implements BiometricServic
final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
- if (provider == null) {
- Slog.w(TAG, "Null provider for removeAll");
- return;
+ for (ServiceProvider provider : mServiceProviders) {
+ List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties();
+ for (FingerprintSensorPropertiesInternal prop : props) {
+ provider.scheduleRemoveAll(prop.sensorId, token, receiver, userId,
+ opPackageName);
+ }
}
- provider.second.scheduleRemoveAll(provider.first, token, receiver, userId,
- opPackageName);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index dfec2e3e308f..0d50499bd02a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -67,7 +67,13 @@ public interface ServiceProvider {
@NonNull
List<FingerprintSensorPropertiesInternal> getSensorProperties();
- @NonNull
+ /**
+ * Returns the internal properties of the specified sensor, if owned by this provider.
+ *
+ * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor.
+ * @return An object representing the internal properties of the specified sensor.
+ */
+ @Nullable
FingerprintSensorPropertiesInternal getSensorProperties(int sensorId);
void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 598cc8992c2d..d798198782ea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -240,10 +242,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
return props;
}
- @NonNull
+ @Nullable
@Override
public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) {
- return mSensors.get(sensorId).getSensorProperties();
+ if (mSensors.size() == 0) {
+ return null;
+ } else if (sensorId == SENSOR_ID_ANY) {
+ return mSensors.valueAt(0).getSensorProperties();
+ } else {
+ final Sensor sensor = mSensors.get(sensorId);
+ return sensor != null ? sensor.getSensorProperties() : null;
+ }
}
private void scheduleLoadAuthenticatorIds(int sensorId) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index ddcfcad59203..adffba280c7f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -63,6 +63,12 @@ class FingerprintResetLockoutClient extends HalClientMonitor<ISession> {
}
@Override
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
+ @Override
protected void startHalOperation() {
try {
getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index a98e7db43f79..b9dee7d1e321 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -475,6 +475,15 @@ class Sensor {
Slog.w(mTag, "setTestHalEnabled: " + enabled);
if (enabled != mTestHalEnabled) {
// The framework should retrieve a new session from the HAL.
+ try {
+ if (mCurrentSession != null && mCurrentSession.mSession != null) {
+ // TODO(181984005): This should be scheduled instead of directly invoked
+ Slog.d(mTag, "Closing old session");
+ mCurrentSession.mSession.close(999 /* cookie */);
+ }
+ } catch (RemoteException e) {
+ Slog.e(mTag, "RemoteException", e);
+ }
mCurrentSession = null;
}
mTestHalEnabled = enabled;
@@ -499,6 +508,11 @@ class Sensor {
proto.end(userToken);
}
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
+ mSensorProperties.resetLockoutRequiresHardwareAuthToken);
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
+ mSensorProperties.resetLockoutRequiresChallenge);
+
proto.end(sensorToken);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index 8547a689dfb1..0b7f3abc9005 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -140,9 +140,5 @@ public class TestHal extends IFingerprint.Stub {
}
};
}
-
- @Override
- public void reset() {
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 6e22a797b435..e737677a7f53 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -347,7 +347,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final @FingerprintSensorProperties.SensorType int sensorType =
mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
: FingerprintSensorProperties.TYPE_REAR;
- // resetLockout is controlled by the framework, so hardwareAuthToken is not required
+ // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
+ // cannot be checked
final boolean resetLockoutRequiresHardwareAuthToken = false;
final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
@@ -515,7 +516,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
return properties;
}
- @NonNull
+ @Nullable
@Override
public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) {
return mSensorProperties;
@@ -526,7 +527,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
- mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
+ final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
+ userId, mContext.getOpPackageName(), sensorId, mLockoutTracker);
+ mScheduler.scheduleClientMonitor(client);
});
}
@@ -758,6 +761,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
proto.end(userToken);
}
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
+ mSensorProperties.resetLockoutRequiresHardwareAuthToken);
+ proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
+ mSensorProperties.resetLockoutRequiresChallenge);
+
proto.end(sensorToken);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
new file mode 100644
index 000000000000..a39f4f8c4d7e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+
+import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+
+/**
+ * Clears lockout, which is handled in the framework (and not the HAL) for the
+ * IBiometricsFingerprint@2.1 interface.
+ */
+public class FingerprintResetLockoutClient extends BaseClientMonitor {
+
+ @NonNull final LockoutFrameworkImpl mLockoutTracker;
+
+ public FingerprintResetLockoutClient(@NonNull Context context, int userId,
+ @NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) {
+ super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
+ sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mLockoutTracker = lockoutTracker;
+ }
+
+ @Override
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+ mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ getTargetUserId());
+ callback.onClientFinished(this, true /* success */);
+ }
+
+ @Override
+ public int getProtoEnum() {
+ return BiometricsProto.CM_RESET_LOCKOUT;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
index 14fdb507b0b1..129f6a61df04 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
@@ -93,8 +93,11 @@ public class TestHal extends IBiometricsFingerprint.Stub {
}
@Override
- public int remove(int gid, int fid) {
+ public int remove(int gid, int fid) throws RemoteException {
Slog.w(TAG, "Remove");
+ if (mCallback != null) {
+ mCallback.onRemoved(0 /* deviceId */, fid, gid, 0 /* remaining */);
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index f44e0691bb9d..95915b7809f3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -95,4 +95,9 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return 0;
}
+
+ @Override
+ public void resetLockout(IBinder token, String opPackageName, int userId,
+ byte[] hardwareAuthToken) throws RemoteException {
+ }
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5020917f8eb1..71565301e3ed 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -22,6 +22,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -43,6 +44,8 @@ import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.Parcel;
@@ -55,9 +58,15 @@ import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.autofill.AutofillManagerInternal;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
import android.widget.Toast;
import com.android.internal.R;
@@ -71,6 +80,7 @@ import com.android.server.wm.WindowManagerInternal;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -86,21 +96,20 @@ class HostClipboardMonitor implements Runnable {
private static final String PIPE_NAME = "pipe:clipboard";
private static final String PIPE_DEVICE = "/dev/qemu_pipe";
+ private static byte[] createOpenHandshake() {
+ // String.getBytes doesn't include the null terminator,
+ // but the QEMU pipe device requires the pipe service name
+ // to be null-terminated.
+
+ final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+ bits[PIPE_NAME.length()] = 0;
+ return bits;
+ }
+
private void openPipe() {
try {
- // String.getBytes doesn't include the null terminator,
- // but the QEMU pipe device requires the pipe service name
- // to be null-terminated.
- byte[] b = new byte[PIPE_NAME.length() + 1];
- b[PIPE_NAME.length()] = 0;
- System.arraycopy(
- PIPE_NAME.getBytes(),
- 0,
- b,
- 0,
- PIPE_NAME.length());
mPipe = new RandomAccessFile(PIPE_DEVICE, "rw");
- mPipe.write(b);
+ mPipe.write(createOpenHandshake());
} catch (IOException e) {
try {
if (mPipe != null) mPipe.close();
@@ -164,11 +173,13 @@ public class ClipboardService extends SystemService {
private static final String TAG = "ClipboardService";
private static final boolean IS_EMULATOR =
- SystemProperties.getBoolean("ro.kernel.qemu", false);
+ SystemProperties.getBoolean("ro.boot.qemu", false);
// DeviceConfig properties
private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+ private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
+ private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
private final ActivityManagerInternal mAmInternal;
private final IUriGrantsManager mUgm;
@@ -179,8 +190,10 @@ public class ClipboardService extends SystemService {
private final AppOpsManager mAppOps;
private final ContentCaptureManagerInternal mContentCaptureInternal;
private final AutofillManagerInternal mAutofillInternal;
+ private final TextClassificationManager mTextClassificationManager;
private final IBinder mPermissionOwner;
private final HostClipboardMonitor mHostClipboardMonitor;
+ private final Handler mWorkerHandler;
@GuardedBy("mLock")
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@@ -188,6 +201,9 @@ public class ClipboardService extends SystemService {
@GuardedBy("mLock")
private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+ @GuardedBy("mLock")
+ private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
+
private final Object mLock = new Object();
/**
@@ -205,6 +221,8 @@ public class ClipboardService extends SystemService {
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);
mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);
+ mTextClassificationManager = (TextClassificationManager)
+ getContext().getSystemService(Context.TEXT_CLASSIFICATION_SERVICE);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
@@ -231,6 +249,10 @@ public class ClipboardService extends SystemService {
updateConfig();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,
getContext().getMainExecutor(), properties -> updateConfig());
+
+ HandlerThread workerThread = new HandlerThread(TAG);
+ workerThread.start();
+ mWorkerHandler = workerThread.getThreadHandler();
}
@Override
@@ -249,6 +271,8 @@ public class ClipboardService extends SystemService {
synchronized (mLock) {
mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+ mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
}
}
@@ -274,6 +298,9 @@ public class ClipboardService extends SystemService {
/** Package of the app that set {@link #primaryClip}. */
String mPrimaryClipPackage;
+ /** Uids that have already triggered a toast notification for {@link #primaryClip} */
+ final SparseBooleanArray mNotifiedUids = new SparseBooleanArray();
+
final HashSet<String> activePermissionOwners
= new HashSet<String>();
@@ -588,6 +615,10 @@ public class ClipboardService extends SystemService {
}
}
+ if (clip != null) {
+ startClassificationLocked(clip);
+ }
+
// Update this user
final int userId = UserHandle.getUserId(uid);
setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -649,6 +680,7 @@ public class ClipboardService extends SystemService {
return;
}
clipboard.primaryClip = clip;
+ clipboard.mNotifiedUids.clear();
if (clip != null) {
clipboard.primaryClipUid = uid;
clipboard.mPrimaryClipPackage = sourcePackage;
@@ -686,6 +718,68 @@ public class ClipboardService extends SystemService {
}
}
+ @GuardedBy("mLock")
+ private void startClassificationLocked(@NonNull ClipData clip) {
+ TextClassifier classifier;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ classifier = mTextClassificationManager.createTextClassificationSession(
+ new TextClassificationContext.Builder(
+ getContext().getPackageName(),
+ TextClassifier.WIDGET_TYPE_CLIPBOARD
+ ).build()
+ );
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (clip.getItemCount() == 0) {
+ clip.getDescription().setClassificationStatus(
+ ClipDescription.CLASSIFICATION_NOT_PERFORMED);
+ return;
+ }
+ CharSequence text = clip.getItemAt(0).getText();
+ if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength
+ || text.length() > classifier.getMaxGenerateLinksTextLength()) {
+ clip.getDescription().setClassificationStatus(
+ ClipDescription.CLASSIFICATION_NOT_PERFORMED);
+ return;
+ }
+
+ mWorkerHandler.post(() -> doClassification(text, clip, classifier));
+ }
+
+ @WorkerThread
+ private void doClassification(
+ CharSequence text, ClipData clip, TextClassifier classifier) {
+ TextLinks.Request request = new TextLinks.Request.Builder(text).build();
+ TextLinks links;
+ try {
+ links = classifier.generateLinks(request);
+ } finally {
+ classifier.destroy();
+ }
+
+ // Find the highest confidence for each entity in the text.
+ ArrayMap<String, Float> confidences = new ArrayMap<>();
+ for (TextLinks.TextLink link : links.getLinks()) {
+ for (int i = 0; i < link.getEntityCount(); i++) {
+ String entity = link.getEntity(i);
+ float conf = link.getConfidenceScore(entity);
+ if (conf > confidences.getOrDefault(entity, 0f)) {
+ confidences.put(entity, conf);
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ clip.getDescription().setConfidenceScores(confidences);
+ if (!links.getLinks().isEmpty()) {
+ clip.getItemAt(0).setTextLinks(links);
+ }
+ }
+ }
+
private boolean isDeviceLocked(@UserIdInt int userId) {
final long token = Binder.clearCallingIdentity();
try {
@@ -917,6 +1011,10 @@ public class ClipboardService extends SystemService {
if (!mShowAccessNotifications) {
return;
}
+ if (Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) == 0) {
+ return;
+ }
// Don't notify if the app accessing the clipboard is the same as the current owner.
if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
return;
@@ -939,35 +1037,42 @@ public class ClipboardService extends SystemService {
&& mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
return;
}
+ // Don't notify if already notified for this uid and clip.
+ if (clipboard.mNotifiedUids.get(uid)) {
+ return;
+ }
+ clipboard.mNotifiedUids.put(uid, true);
+
+ Binder.withCleanCallingIdentity(() -> {
+ // Retrieve the app label of the source of the clip data
+ CharSequence sourceAppLabel = null;
+ if (clipboard.mPrimaryClipPackage != null) {
+ try {
+ sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
+ clipboard.mPrimaryClipPackage, 0, userId));
+ } catch (PackageManager.NameNotFoundException e) {
+ // leave label as null
+ }
+ }
- // Retrieve the app label of the source of the clip data
- CharSequence sourceAppLabel = null;
- if (clipboard.mPrimaryClipPackage != null) {
try {
- sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
- clipboard.mPrimaryClipPackage, 0, userId));
+ CharSequence callingAppLabel = mPm.getApplicationLabel(
+ mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
+ String message;
+ if (sourceAppLabel != null) {
+ message = getContext().getString(
+ R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
+ } else {
+ message = getContext().getString(
+ R.string.pasted_from_clipboard, callingAppLabel);
+ }
+ Slog.i(TAG, message);
+ Toast.makeText(
+ getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT)
+ .show();
} catch (PackageManager.NameNotFoundException e) {
- // leave label as null
+ // do nothing
}
- }
-
- try {
- CharSequence callingAppLabel = mPm.getApplicationLabel(
- mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
- String message;
- if (sourceAppLabel != null) {
- message = getContext().getString(
- R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
- } else {
- message = getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
- }
- Slog.i(TAG, message);
- Binder.withCleanCallingIdentity(() ->
- Toast.makeText(getContext(), UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT)
- .show());
- } catch (PackageManager.NameNotFoundException e) {
- // do nothing
- }
+ });
}
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 5cf478a3ef1f..ae9b0015de43 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
@@ -55,7 +56,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* A change ID to be used only in the CTS test for this SystemApi
*/
@ChangeId
- @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion.
+ @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion.
static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
/**
@@ -233,7 +234,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param app Info about the app in question
* @return {@code true} if the change should be enabled for the package.
*/
- boolean isEnabled(ApplicationInfo app) {
+ boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) {
if (app == null) {
return defaultValue();
}
@@ -244,7 +245,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
return false;
}
if (getEnableSinceTargetSdk() != -1) {
- return app.targetSdkVersion >= getEnableSinceTargetSdk();
+ // If the change is gated by a platform version newer than the one currently installed
+ // on the device, disregard the app's target sdk version.
+ int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk());
+ if (compareSdk != app.targetSdkVersion) {
+ compareSdk = app.targetSdkVersion;
+ }
+ return compareSdk >= getEnableSinceTargetSdk();
}
return true;
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 2c053b421904..ef86f42d6c3c 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -74,12 +74,14 @@ final class CompatConfig {
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private final AndroidBuildClassifier mAndroidBuildClassifier;
private Context mContext;
private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
+ mAndroidBuildClassifier = androidBuildClassifier;
mContext = context;
}
@@ -133,7 +135,7 @@ final class CompatConfig {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i);
- if (!c.isEnabled(app)) {
+ if (!c.isEnabled(app, mAndroidBuildClassifier)) {
disabled.add(c.getId());
}
}
@@ -175,7 +177,7 @@ final class CompatConfig {
// we know nothing about this change: default behaviour is enabled.
return true;
}
- return c.isEnabled(app);
+ return c.isEnabled(app, mAndroidBuildClassifier);
}
}
@@ -475,7 +477,7 @@ final class CompatConfig {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i);
- if (c.isEnabled(applicationInfo)) {
+ if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
enabled.add(c.getId());
} else {
disabled.add(c.getId());
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index fe5b4a98797d..aa66a1a8b01f 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -85,6 +86,9 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
if (debuggableBuild) {
return new OverrideAllowedState(ALLOWED, -1, -1);
}
+ if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
+ return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
+ }
PackageManager packageManager = mContext.getPackageManager();
if (packageManager == null) {
throw new IllegalStateException("No PackageManager!");
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index d17753fe81bd..2be39aa24294 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -66,18 +66,22 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final Context mContext;
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
+ private final AndroidBuildClassifier mBuildClassifier;
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
- mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+ mBuildClassifier = new AndroidBuildClassifier();
+ mCompatConfig = CompatConfig.create(mBuildClassifier, mContext);
}
@VisibleForTesting
- PlatformCompat(Context context, CompatConfig compatConfig) {
+ PlatformCompat(Context context, CompatConfig compatConfig,
+ AndroidBuildClassifier buildClassifier) {
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig;
+ mBuildClassifier = buildClassifier;
registerPackageReceiver(context);
}
@@ -392,7 +396,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return false;
}
if (change.getEnableSinceTargetSdk() > 0) {
- return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
+ return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q
+ && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk();
}
return true;
}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 4f6b5301e56f..702434ba07b7 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
@@ -33,6 +32,7 @@ import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityManager;
import android.net.IDnsResolver;
import android.net.InetAddresses;
import android.net.LinkProperties;
@@ -127,13 +127,17 @@ public class DnsManager {
private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
- public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
- final String mode = getPrivateDnsMode(cr);
+ /**
+ * Get PrivateDnsConfig.
+ */
+ public static PrivateDnsConfig getPrivateDnsConfig(Context context) {
+ final String mode = ConnectivityManager.getPrivateDnsMode(context);
final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
- final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
+ final String specifier = getStringSetting(context.getContentResolver(),
+ PRIVATE_DNS_SPECIFIER);
return new PrivateDnsConfig(specifier, null);
}
@@ -268,7 +272,7 @@ public class DnsManager {
}
public PrivateDnsConfig getPrivateDnsConfig() {
- return getPrivateDnsConfig(mContentResolver);
+ return getPrivateDnsConfig(mContext);
}
public void removeNetwork(Network network) {
@@ -479,13 +483,6 @@ public class DnsManager {
return result;
}
- private static String getPrivateDnsMode(ContentResolver cr) {
- String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
- if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
- if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
- return mode;
- }
-
private static String getStringSetting(ContentResolver cr, String which) {
return Settings.Global.getString(cr, which);
}
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index ef767341d609..a25b89ac039a 100644
--- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -17,7 +17,6 @@
package com.android.server.connectivity;
import android.os.SystemProperties;
-import android.sysprop.NetworkProperties;
public class MockableSystemProperties {
@@ -32,10 +31,4 @@ public class MockableSystemProperties {
public boolean getBoolean(String key, boolean def) {
return SystemProperties.getBoolean(key, def);
}
- /**
- * Set net.tcp_def_init_rwnd to the tcp initial receive window size.
- */
- public void setTcpInitRwnd(int value) {
- NetworkProperties.tcp_init_rwnd(value);
- }
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index fa80b25f9026..c66a280f2b02 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
import static com.android.net.module.util.CollectionUtils.contains;
import android.annotation.NonNull;
@@ -35,6 +37,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetworkStackConstants;
+import com.android.server.ConnectivityService;
import java.net.Inet6Address;
import java.util.Objects;
@@ -94,12 +97,15 @@ public class Nat464Xlat {
private Inet6Address mIPv6Address;
private State mState = State.IDLE;
+ private boolean mEnableClatOnCellular;
private boolean mPrefixDiscoveryRunning;
- public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver) {
+ public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
+ ConnectivityService.Dependencies deps) {
mDnsResolver = dnsResolver;
mNetd = netd;
mNetwork = nai;
+ mEnableClatOnCellular = deps.getCellular464XlatEnabled();
}
/**
@@ -111,7 +117,7 @@ public class Nat464Xlat {
* @return true if the network requires clat, false otherwise.
*/
@VisibleForTesting
- protected static boolean requiresClat(NetworkAgentInfo nai) {
+ protected boolean requiresClat(NetworkAgentInfo nai) {
// TODO: migrate to NetworkCapabilities.TRANSPORT_*.
final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType());
final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState());
@@ -126,7 +132,9 @@ public class Nat464Xlat {
final boolean skip464xlat = (nai.netAgentConfig() != null)
&& nai.netAgentConfig().skip464xlat;
- return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
+ return supported && connected && isIpv6OnlyNetwork && !skip464xlat
+ && (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
+ ? isCellular464XlatEnabled() : true);
}
/**
@@ -137,7 +145,7 @@ public class Nat464Xlat {
* @return true if the network should start clat, false otherwise.
*/
@VisibleForTesting
- protected static boolean shouldStartClat(NetworkAgentInfo nai) {
+ protected boolean shouldStartClat(NetworkAgentInfo nai) {
LinkProperties lp = nai.linkProperties;
return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
}
@@ -507,4 +515,9 @@ public class Nat464Xlat {
protected int getNetId() {
return mNetwork.network.getNetId();
}
+
+ @VisibleForTesting
+ protected boolean isCellular464XlatEnabled() {
+ return mEnableClatOnCellular;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1d0e11569c80..e44dcf5975f1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -35,6 +35,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
+import android.net.NetworkScore;
import android.net.NetworkStateSnapshot;
import android.net.QosCallbackException;
import android.net.QosFilter;
@@ -302,8 +303,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// validated).
private boolean mInactive;
- // This represents the quality of the network with no clear scale.
- private int mScore;
+ // This represents the quality of the network.
+ private NetworkScore mScore;
// The list of NetworkRequests being satisfied by this Network.
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -338,10 +339,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private final QosCallbackTracker mQosCallbackTracker;
public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
- @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
+ @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
+ @NonNull NetworkScore score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
- QosCallbackTracker qosCallbackTracker) {
+ QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
Objects.requireNonNull(net);
Objects.requireNonNull(info);
Objects.requireNonNull(lp);
@@ -355,7 +357,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
linkProperties = lp;
networkCapabilities = nc;
mScore = score;
- clatd = new Nat464Xlat(this, netd, dnsResolver);
+ clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
mConnService = connService;
mContext = context;
mHandler = handler;
@@ -603,9 +605,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
}
@Override
- public void sendScore(int score) {
- mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, score, 0,
- new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+ public void sendScore(@NonNull final NetworkScore score) {
+ mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED,
+ new Pair<>(NetworkAgentInfo.this, score)).sendToTarget();
}
@Override
@@ -717,6 +719,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
break;
case LISTEN:
+ case LISTEN_FOR_BEST:
case TRACK_DEFAULT:
case TRACK_SYSTEM_DEFAULT:
break;
@@ -857,7 +860,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
- int score = mScore;
+ int score = mScore.getLegacyInt();
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
@@ -886,7 +889,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
return getCurrentScore(true);
}
- public void setScore(final int score) {
+ public void setScore(final NetworkScore score) {
mScore = score;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5e6b9f39b40a..2e51be39bfae 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -36,7 +36,7 @@ import android.text.TextUtils;
import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.TrafficStatsConstants;
+import com.android.net.module.util.NetworkStackConstants;
import libcore.io.IoUtils;
@@ -446,7 +446,7 @@ public class NetworkDiagnostics {
int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
throws ErrnoException, IOException {
final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_PROBE);
+ NetworkStackConstants.TAG_SYSTEM_PROBE);
try {
mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
} finally {
@@ -745,7 +745,7 @@ public class NetworkDiagnostics {
if (ensureMeasurementNecessary()) return;
// No need to restore the tag, since this thread is only used for this measurement.
- TrafficStats.getAndSetThreadStatsTag(TrafficStatsConstants.TAG_SYSTEM_PROBE);
+ TrafficStats.getAndSetThreadStatsTag(NetworkStackConstants.TAG_SYSTEM_PROBE);
try (SSLSocket sslSocket = setupSSLSocket()) {
sendDoTProbe(sslSocket);
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyService.java
index aadaf4d9584f..d23b488f3054 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyService.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -26,12 +28,16 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.net.IPacProxyInstalledListener;
+import android.net.IPacProxyManager;
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -44,6 +50,7 @@ import com.android.internal.util.TrafficStatsConstants;
import com.android.net.IProxyCallback;
import com.android.net.IProxyPortListener;
import com.android.net.IProxyService;
+import com.android.net.module.util.PermissionUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -53,7 +60,7 @@ import java.net.URLConnection;
/**
* @hide
*/
-public class PacProxyInstaller {
+public class PacProxyService extends IPacProxyManager.Stub {
private static final String PAC_PACKAGE = "com.android.pacprocessor";
private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
@@ -61,7 +68,7 @@ public class PacProxyInstaller {
private static final String PROXY_PACKAGE = "com.android.proxyhandler";
private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
- private static final String TAG = "PacProxyInstaller";
+ private static final String TAG = "PacProxyService";
private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
@@ -71,10 +78,6 @@ public class PacProxyInstaller {
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
- // Return values for #setCurrentProxyScriptUrl
- public static final boolean DONT_SEND_BROADCAST = false;
- public static final boolean DO_SEND_BROADCAST = true;
-
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
@@ -93,8 +96,8 @@ public class PacProxyInstaller {
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private Handler mConnectivityHandler;
- private final int mProxyMessage;
+ private final RemoteCallbackList<IPacProxyInstalledListener>
+ mCallbacks = new RemoteCallbackList<>();
/**
* Used for locking when setting mProxyService and all references to mCurrentPac.
@@ -102,6 +105,13 @@ public class PacProxyInstaller {
private final Object mProxyLock = new Object();
/**
+ * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
+ * last URL and port, and the broadcast message being sent with the correct arguments.
+ * TODO : this should probably protect all instances of these variables
+ */
+ private final Object mBroadcastStateLock = new Object();
+
+ /**
* Runnable to download PAC script.
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
* latest data fetched from mPacUrl is stored in mProxyService.
@@ -146,10 +156,10 @@ public class PacProxyInstaller {
}
}
- public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
+ public PacProxyService(@NonNull Context context) {
mContext = context;
mLastPort = -1;
- final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
+ final HandlerThread netThread = new HandlerThread("android.pacproxyservice",
android.os.Process.THREAD_PRIORITY_DEFAULT);
netThread.start();
mNetThreadHandler = new Handler(netThread.getLooper());
@@ -158,8 +168,6 @@ public class PacProxyInstaller {
context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
context.registerReceiver(new PacRefreshIntentReceiver(),
new IntentFilter(ACTION_PAC_REFRESH));
- mConnectivityHandler = handler;
- mProxyMessage = proxyMessage;
}
private AlarmManager getAlarmManager() {
@@ -169,38 +177,52 @@ public class PacProxyInstaller {
return mAlarmManager;
}
+ @Override
+ public void addListener(IPacProxyInstalledListener listener) {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
+ mCallbacks.register(listener);
+ }
+
+ @Override
+ public void removeListener(IPacProxyInstalledListener listener) {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
+ mCallbacks.unregister(listener);
+ }
+
/**
* Updates the PAC Proxy Installer with current Proxy information. This is called by
- * the ProxyTracker directly before a broadcast takes place to allow
- * the PacProxyInstaller to indicate that the broadcast should not be sent and the
- * PacProxyInstaller will trigger a new broadcast when it is ready.
+ * the ProxyTracker through PacProxyManager before a broadcast takes place to allow
+ * the PacProxyService to indicate that the broadcast should not be sent and the
+ * PacProxyService will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
- * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
*/
- public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
- if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
- // Allow to send broadcast, nothing to do.
- return DO_SEND_BROADCAST;
- }
- mPacUrl = proxy.getPacFileUrl();
- mCurrentDelay = DELAY_1;
- mHasSentBroadcast = false;
- mHasDownloaded = false;
- getAlarmManager().cancel(mPacRefreshIntent);
- bind();
- return DONT_SEND_BROADCAST;
- } else {
- getAlarmManager().cancel(mPacRefreshIntent);
- synchronized (mProxyLock) {
- mPacUrl = Uri.EMPTY;
- mCurrentPac = null;
- if (mProxyService != null) {
- unbind();
+ @Override
+ public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
+
+ synchronized (mBroadcastStateLock) {
+ if (proxy != null && !Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ mHasSentBroadcast = false;
+ mHasDownloaded = false;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ bind();
+ } else {
+ getAlarmManager().cancel(mPacRefreshIntent);
+ synchronized (mProxyLock) {
+ mPacUrl = Uri.EMPTY;
+ mCurrentPac = null;
+ if (mProxyService != null) {
+ unbind();
+ }
}
}
- return DO_SEND_BROADCAST;
}
}
@@ -275,6 +297,7 @@ public class PacProxyInstaller {
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
}
+ @GuardedBy("mProxyLock")
private void setCurrentProxyScript(String script) {
if (mProxyService == null) {
Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -347,6 +370,9 @@ public class PacProxyInstaller {
public void setProxyPort(int port) {
if (mLastPort != -1) {
// Always need to send if port changed
+ // TODO: Here lacks synchronization because this write cannot
+ // guarantee that it's visible from sendProxyIfNeeded() when
+ // it's called by a Runnable which is post by mNetThread.
mHasSentBroadcast = false;
}
mLastPort = port;
@@ -365,8 +391,9 @@ public class PacProxyInstaller {
}
}
};
- mContext.bindService(intent, mProxyConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
+ mContext.bindService(intent,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+ new HandlerExecutor(mNetThreadHandler), mProxyConnection);
}
private void unbind() {
@@ -383,16 +410,28 @@ public class PacProxyInstaller {
}
private void sendPacBroadcast(ProxyInfo proxy) {
- mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
+ final int length = mCallbacks.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final IPacProxyInstalledListener listener = mCallbacks.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onPacProxyInstalled(null /* network */, proxy);
+ } catch (RemoteException ignored) { }
+ }
+ }
+ mCallbacks.finishBroadcast();
}
- private synchronized void sendProxyIfNeeded() {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
+ // This method must be called on mNetThreadHandler.
+ private void sendProxyIfNeeded() {
+ synchronized (mBroadcastStateLock) {
+ if (!mHasDownloaded || (mLastPort == -1)) {
+ return;
+ }
+ if (!mHasSentBroadcast) {
+ sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+ mHasSentBroadcast = true;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 9411e33434d8..488677ac1b59 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -31,14 +31,17 @@ import static android.os.Process.SYSTEM_UID;
import static com.android.net.module.util.CollectionUtils.toIntArray;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
import android.net.INetd;
import android.net.UidRange;
+import android.net.Uri;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -54,7 +57,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.CollectionUtils;
-import com.android.server.LocalServices;
import java.util.ArrayList;
import java.util.HashMap;
@@ -71,7 +73,7 @@ import java.util.Set;
*
* @hide
*/
-public class PermissionMonitor implements PackageManagerInternal.PackageListObserver {
+public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
private static final boolean DBG = true;
protected static final Boolean SYSTEM = Boolean.TRUE;
@@ -83,6 +85,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
private final SystemConfigManager mSystemConfigManager;
private final INetd mNetd;
private final Dependencies mDeps;
+ private final Context mContext;
@GuardedBy("this")
private final Set<UserHandle> mUsers = new HashSet<>();
@@ -102,6 +105,25 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
@GuardedBy("this")
private final Set<Integer> mAllApps = new HashSet<>();
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, uid);
+ } else {
+ Log.wtf(TAG, "received unexpected intent: " + action);
+ }
+ }
+ };
+
/**
* Dependencies of PermissionMonitor, for injection in tests.
*/
@@ -127,6 +149,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
mNetd = netd;
mDeps = deps;
+ mContext = context;
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -134,12 +157,14 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
public synchronized void startMonitoring() {
log("Monitoring");
- PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- if (pmi != null) {
- pmi.getPackageList(this);
- } else {
- loge("failed to get the PackageManagerInternal service");
- }
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver(
+ mIntentReceiver, intentFilter, null /* broadcastPermission */,
+ null /* scheduler */);
+
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
| MATCH_ANY_USER);
if (apps == null) {
@@ -347,9 +372,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
*
* @hide
*/
- @Override
public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
- sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+ // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+ // using appId instead of uid actually
+ sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
@@ -384,9 +410,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
*
* @hide
*/
- @Override
public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
- sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+ // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+ // using appId instead of uid actually
+ sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
// If the newly-removed package falls within some VPN's uid range, update Netd with it.
// This needs to happen before the mApps update below, since removeBypassingUids() depends
@@ -432,19 +459,6 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
}
}
- /**
- * Called when a package is changed.
- *
- * @param packageName The name of the changed package.
- * @param uid The uid of the changed package.
- *
- * @hide
- */
- @Override
- public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) {
- sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
- }
-
private static int getNetdPermissionMask(String[] requestedPermissions,
int[] requestedPermissionsFlags) {
int permissions = 0;
diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
new file mode 100644
index 000000000000..dd2815d9e2e3
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.UserHandle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A data class containing all the per-profile network preferences.
+ *
+ * A given profile can only have one preference.
+ */
+public class ProfileNetworkPreferences {
+ /**
+ * A single preference, as it applies to a given user profile.
+ */
+ public static class Preference {
+ @NonNull public final UserHandle user;
+ // Capabilities are only null when sending an object to remove the setting for a user
+ @Nullable public final NetworkCapabilities capabilities;
+
+ public Preference(@NonNull final UserHandle user,
+ @Nullable final NetworkCapabilities capabilities) {
+ this.user = user;
+ this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
+ }
+
+ /** toString */
+ public String toString() {
+ return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]";
+ }
+ }
+
+ @NonNull public final List<Preference> preferences;
+
+ public ProfileNetworkPreferences() {
+ preferences = Collections.EMPTY_LIST;
+ }
+
+ private ProfileNetworkPreferences(@NonNull final List<Preference> list) {
+ preferences = Collections.unmodifiableList(list);
+ }
+
+ /**
+ * Returns a new object consisting of this object plus the passed preference.
+ *
+ * If a preference already exists for the same user, it will be replaced by the passed
+ * preference. Passing a Preference object containing a null capabilities object is equivalent
+ * to (and indeed, implemented as) removing the preference for this user.
+ */
+ public ProfileNetworkPreferences plus(@NonNull final Preference pref) {
+ final ArrayList<Preference> newPrefs = new ArrayList<>();
+ for (final Preference existingPref : preferences) {
+ if (!existingPref.user.equals(pref.user)) {
+ newPrefs.add(existingPref);
+ }
+ }
+ if (null != pref.capabilities) {
+ newPrefs.add(pref);
+ }
+ return new ProfileNetworkPreferences(newPrefs);
+ }
+
+ public boolean isEmpty() {
+ return preferences.isEmpty();
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index d83ff837d9be..8b9c83678777 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -27,15 +27,19 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.Network;
+import android.net.PacProxyManager;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.net.module.util.ProxyUtils;
@@ -67,7 +71,7 @@ public class ProxyTracker {
// is not set. Individual networks have their own settings that override this. This member
// is set through setDefaultProxy, which is called when the default network changes proxies
// in its LinkProperties, or when ConnectivityService switches to a new default network, or
- // when PacProxyInstaller resolves the proxy.
+ // when PacProxyService resolves the proxy.
@Nullable
@GuardedBy("mProxyLock")
private volatile ProxyInfo mDefaultProxy = null;
@@ -77,16 +81,31 @@ public class ProxyTracker {
private final Handler mConnectivityServiceHandler;
- // The object responsible for Proxy Auto Configuration (PAC).
- @NonNull
- private final PacProxyInstaller mPacProxyInstaller;
+ private final PacProxyManager mPacProxyManager;
+
+ private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
+ private final int mEvent;
+
+ PacProxyInstalledListener(int event) {
+ mEvent = event;
+ }
+
+ public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
+ mConnectivityServiceHandler
+ .sendMessage(mConnectivityServiceHandler
+ .obtainMessage(mEvent, new Pair<>(network, proxy)));
+ }
+ }
public ProxyTracker(@NonNull final Context context,
@NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
mContext = context;
mConnectivityServiceHandler = connectivityServiceInternalHandler;
- mPacProxyInstaller = new PacProxyInstaller(
- context, connectivityServiceInternalHandler, pacChangedEvent);
+ mPacProxyManager = context.getSystemService(PacProxyManager.class);
+
+ PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
+ mPacProxyManager.addPacProxyInstalledListener(
+ new HandlerExecutor(mConnectivityServiceHandler), listener);
}
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -182,7 +201,7 @@ public class ProxyTracker {
if (!TextUtils.isEmpty(pacFileUrl)) {
mConnectivityServiceHandler.post(
- () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
+ () -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
}
}
}
@@ -226,9 +245,9 @@ public class ProxyTracker {
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
+ mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
- if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
- == PacProxyInstaller.DONT_SEND_BROADCAST) {
+ if (!shouldSendBroadcast(proxyInfo)) {
return;
}
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,6 +263,10 @@ public class ProxyTracker {
}
}
+ private boolean shouldSendBroadcast(ProxyInfo proxy) {
+ return Uri.EMPTY.equals(proxy.getPacFileUrl()) || proxy.getPort() > 0;
+ }
+
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
@@ -308,10 +331,10 @@ public class ProxyTracker {
return;
}
- // This call could be coming from the PacProxyInstaller, containing the port of the
+ // This call could be coming from the PacProxyService, containing the port of the
// local proxy. If this new proxy matches the global proxy then copy this proxy to the
// global (to get the correct local port), and send a broadcast.
- // TODO: Switch PacProxyInstaller to have its own message to send back rather than
+ // TODO: Switch PacProxyService to have its own message to send back rather than
// reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
if ((mGlobalProxy != null) && (proxyInfo != null)
&& (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 027b9afba392..0e714969a69b 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -791,12 +791,13 @@ public final class ContentService extends IContentService.Stub {
public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
enforceCrossUserPermission(userId,
"no permission to read sync settings for user " + userId);
+ final int callingUid = Binder.getCallingUid();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
final long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
- return syncManager.getSyncAdapterTypes(userId);
+ return syncManager.getSyncAdapterTypes(callingUid, userId);
} finally {
restoreCallingIdentity(identityToken);
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index df870125e253..ac7e01ed4d72 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -89,6 +89,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
+import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.EventLog;
import android.util.Log;
@@ -1257,16 +1258,19 @@ public class SyncManager {
syncExemptionFlag, callingUid, callingPid, callingPackage);
}
- public SyncAdapterType[] getSyncAdapterTypes(int userId) {
+ public SyncAdapterType[] getSyncAdapterTypes(int callingUid, int userId) {
final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
serviceInfos = mSyncAdapters.getAllServices(userId);
- SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
- int i = 0;
+ final List<SyncAdapterType> types = new ArrayList<>(serviceInfos.size());
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
- types[i] = serviceInfo.type;
- ++i;
+ final String packageName = serviceInfo.type.getPackageName();
+ if (!TextUtils.isEmpty(packageName) && mPackageManagerInternal.filterAppAccess(
+ packageName, callingUid, userId)) {
+ continue;
+ }
+ types.add(serviceInfo.type);
}
- return types;
+ return types.toArray(new SyncAdapterType[] {});
}
public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 217f1cd56598..a8b0994402e8 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -42,6 +42,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.SystemService;
import com.android.server.policy.DeviceStatePolicyImpl;
@@ -447,6 +448,9 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
+ newState.getIdentifier(), !mCommittedState.isPresent());
+
mCommittedState = Optional.of(newState);
mPendingState = Optional.empty();
updatePendingStateLocked();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index a2b9b966dd46..91b96dc17473 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -46,7 +46,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
-import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import java.io.PrintWriter;
@@ -220,12 +219,14 @@ class AutomaticBrightnessController {
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
+ HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
+ HighBrightnessModeController hbmController) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
- ambientBrightnessThresholds, screenBrightnessThresholds, display, context
+ ambientBrightnessThresholds, screenBrightnessThresholds, display, context,
+ hbmController
);
}
@@ -236,7 +237,8 @@ class AutomaticBrightnessController {
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
+ HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
+ HighBrightnessModeController hbmController) {
mInjector = injector;
mContext = context;
mCallbacks = callbacks;
@@ -273,20 +275,7 @@ class AutomaticBrightnessController {
mPendingForegroundAppPackageName = null;
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-
- final DisplayDeviceConfig ddConfig =
- display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
- HighBrightnessModeData hbmData =
- ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
-
- final Runnable hbmChangeCallback = () -> {
- updateAutoBrightness(true /*sendUpdate*/, false /*userInitiatedChange*/);
- // TODO: b/175937645 - Callback to DisplayManagerService to indicate a change to the HBM
- // allowance has been made so that the brightness limits can be calculated
- // appropriately.
- };
- mHbmController = new HighBrightnessModeController(mHandler, brightnessMin, brightnessMax,
- hbmData, hbmChangeCallback);
+ mHbmController = hbmController;
}
/**
@@ -327,6 +316,7 @@ class AutomaticBrightnessController {
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
float brightness, boolean userChangedBrightness, float adjustment,
boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
+ mHbmController.setAutoBrightnessEnabled(enable);
// While dozing, the application processor may be suspended which will prevent us from
// receiving new information from the light sensor. On some devices, we may be able to
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 06010f51e231..251b57947195 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -110,6 +110,7 @@ public class BrightnessTracker {
private static final String ATTR_TIMESTAMP = "timestamp";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_USER = "user";
+ private static final String ATTR_UNIQUE_DISPLAY_ID = "uniqueDisplayId";
private static final String ATTR_LUX = "lux";
private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
@@ -217,6 +218,9 @@ public class BrightnessTracker {
}
private void backgroundStart(float initialBrightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "Background start");
+ }
readEvents();
readAmbientBrightnessStats();
@@ -311,7 +315,7 @@ public class BrightnessTracker {
*/
public void notifyBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig) {
+ boolean isDefaultBrightnessConfig, String uniqueDisplayId) {
if (DEBUG) {
Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
brightness, userInitiated));
@@ -319,13 +323,13 @@ public class BrightnessTracker {
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
- mInjector.currentTimeMillis()));
+ mInjector.currentTimeMillis(), uniqueDisplayId));
m.sendToTarget();
}
private void handleBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig, long timestamp) {
+ boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) {
BrightnessChangeEvent.Builder builder;
synchronized (mDataCollectionLock) {
@@ -350,6 +354,7 @@ public class BrightnessTracker {
builder.setPowerBrightnessFactor(powerBrightnessFactor);
builder.setUserBrightnessPoint(isUserSetBrightness);
builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
+ builder.setUniqueDisplayId(uniqueDisplayId);
final int readingCount = mLastSensorReadings.size();
if (readingCount == 0) {
@@ -562,6 +567,7 @@ public class BrightnessTracker {
out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp);
out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
out.attributeInt(null, ATTR_USER, userSerialNo);
+ out.attribute(null, ATTR_UNIQUE_DISPLAY_ID, toWrite[i].uniqueDisplayId);
out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel);
out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
@@ -646,6 +652,8 @@ public class BrightnessTracker {
builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
builder.setUserId(mInjector.getUserId(mUserManager,
parser.getAttributeInt(null, ATTR_USER)));
+ builder.setUniqueDisplayId(
+ parser.getAttributeValue(null, ATTR_UNIQUE_DISPLAY_ID));
builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL));
builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
builder.setColorTemperature(
@@ -980,7 +988,8 @@ public class BrightnessTracker {
boolean userInitiatedChange = (msg.arg1 == 1);
handleBrightnessChanged(values.brightness, userInitiatedChange,
values.powerBrightnessFactor, values.isUserSetBrightness,
- values.isDefaultBrightnessConfig, values.timestamp);
+ values.isDefaultBrightnessConfig, values.timestamp,
+ values.uniqueDisplayId);
break;
case MSG_START_SENSOR_LISTENER:
startSensorListener();
@@ -1007,20 +1016,22 @@ public class BrightnessTracker {
}
private static class BrightnessChangeValues {
- final float brightness;
- final float powerBrightnessFactor;
- final boolean isUserSetBrightness;
- final boolean isDefaultBrightnessConfig;
- final long timestamp;
+ public final float brightness;
+ public final float powerBrightnessFactor;
+ public final boolean isUserSetBrightness;
+ public final boolean isDefaultBrightnessConfig;
+ public final long timestamp;
+ public final String uniqueDisplayId;
BrightnessChangeValues(float brightness, float powerBrightnessFactor,
boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
- long timestamp) {
+ long timestamp, String uniqueDisplayId) {
this.brightness = brightness;
this.powerBrightnessFactor = powerBrightnessFactor;
this.isUserSetBrightness = isUserSetBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.timestamp = timestamp;
+ this.uniqueDisplayId = uniqueDisplayId;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 174d4b2fe00d..82ca820ec4d7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -376,6 +376,8 @@ public final class DisplayManagerService extends SystemService {
private final ColorSpace mWideColorSpace;
private SensorManager mSensorManager;
+ private BrightnessTracker mBrightnessTracker;
+
// Whether minimal post processing is allowed by the user.
@GuardedBy("mSyncRoot")
@@ -1128,7 +1130,7 @@ public final class DisplayManagerService extends SystemService {
recordTopInsetLocked(display);
}
addDisplayPowerControllerLocked(display);
- mDisplayStates.append(displayId, Display.STATE_OFF);
+ mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1162,7 +1164,7 @@ public final class DisplayManagerService extends SystemService {
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChanged();
+ dpc.onDisplayChangedLocked();
}
}
@@ -1204,16 +1206,15 @@ public final class DisplayManagerService extends SystemService {
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
- final int state;
final int displayId = display.getDisplayIdLocked();
+ final int state = mDisplayStates.get(displayId);
- if (display.isEnabled()) {
- state = mDisplayStates.get(displayId);
- } else {
- state = Display.STATE_OFF;
+ // Only send a request for display state if the display state has already been
+ // initialized by DisplayPowercontroller.
+ if (state != Display.STATE_UNKNOWN) {
+ final float brightness = mDisplayBrightnesses.get(displayId);
+ return device.requestDisplayStateLocked(state, brightness);
}
- final float brightness = mDisplayBrightnesses.get(displayId);
- return device.requestDisplayStateLocked(state, brightness);
}
return null;
}
@@ -1852,7 +1853,10 @@ public final class DisplayManagerService extends SystemService {
for (int i = 0; i < displayPowerControllerCount; i++) {
mDisplayPowerControllers.valueAt(i).dump(pw);
}
-
+ if (mBrightnessTracker != null) {
+ pw.println();
+ mBrightnessTracker.dump(pw);
+ }
pw.println();
mPersistentDataStore.dump(pw);
}
@@ -1938,9 +1942,12 @@ public final class DisplayManagerService extends SystemService {
// initPowerManagement has not yet been called.
return;
}
+ if (mBrightnessTracker == null) {
+ mBrightnessTracker = new BrightnessTracker(mContext, null);
+ }
final DisplayPowerController displayPowerController = new DisplayPowerController(
mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
- mDisplayBlanker, display);
+ mDisplayBlanker, display, mBrightnessTracker);
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 011732682ace..7110d3e6a7c1 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -174,6 +174,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// The ID of the LogicalDisplay tied to this DisplayPowerController.
private final int mDisplayId;
+ // The unique ID of the primary display device currently tied to this logical display
+ private String mUniqueDisplayId;
+
// Tracker for brightness changes.
@Nullable
private final BrightnessTracker mBrightnessTracker;
@@ -350,12 +353,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private final ColorDisplayServiceInternal mCdsi;
private final float[] mNitsRange;
+ private final HighBrightnessModeController mHbmController;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
// The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
private float mInitialAutoBrightness;
+
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -413,16 +419,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
*/
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,
- SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay) {
+ SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
+ BrightnessTracker brightnessTracker) {
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mHandler = new DisplayControllerHandler(handler.getLooper());
if (mDisplayId == Display.DEFAULT_DISPLAY) {
- mBrightnessTracker = new BrightnessTracker(context, null);
mBatteryStats = BatteryStatsService.getService();
} else {
- mBrightnessTracker = null;
mBatteryStats = null;
}
@@ -432,6 +437,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
mContext = context;
+ mBrightnessTracker = brightnessTracker;
+
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -476,6 +483,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mSkipScreenOnBrightnessRamp = resources.getBoolean(
com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
+ mHbmController = createHbmController();
+
if (mUseSoftwareAutoBrightnessConfig) {
final float dozeScaleFactor = resources.getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
@@ -535,7 +544,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds, logicalDisplay, context);
+ screenBrightnessThresholds, logicalDisplay, context, mHbmController);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -750,8 +759,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* when displays get swapped on foldable devices. For example, different brightness properties
* of each display need to be properly reflected in AutomaticBrightnessController.
*/
- public void onDisplayChanged() {
+ public void onDisplayChangedLocked() {
// TODO: b/175821789 - Support high brightness on multiple (folding) displays
+
+ mUniqueDisplayId = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
}
/**
@@ -774,10 +785,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessController.stop();
}
- if (mBrightnessTracker != null) {
- mBrightnessTracker.stop();
- }
-
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
}
}
@@ -982,7 +989,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mWaitingForNegativeProximity = false;
mIgnoreProximityUntilChanged = false;
}
- if (mScreenOffBecauseOfProximity) {
+
+ if (!mLogicalDisplay.isEnabled() || mScreenOffBecauseOfProximity) {
state = Display.STATE_OFF;
}
@@ -1080,6 +1088,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
}
+ boolean updateScreenBrightnessSetting = false;
+
// Apply auto-brightness.
boolean slowChange = false;
if (Float.isNaN(brightnessState)) {
@@ -1096,11 +1106,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
slowChange = true; // slowly adapt to auto-brightness
}
- // Tell the rest of the system about the new brightness. Note that we do this
- // before applying the low power or dim transformations so that the slider
- // accurately represents the full possible range, even if they range changes what
- // it means in absolute terms.
- putScreenBrightnessSetting(brightnessState);
+ updateScreenBrightnessSetting = true;
mAppliedAutoBrightness = true;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
} else {
@@ -1118,6 +1124,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAppliedAutoBrightness = false;
brightnessAdjustmentFlags = 0;
}
+
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
@@ -1128,9 +1135,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ if (brightnessState != mCurrentScreenBrightnessSetting) {
+ // The manually chosen screen brightness is outside of the currently allowed
+ // range (i.e., high-brightness-mode), make sure we tell the rest of the system
+ // by updating the setting.
+ updateScreenBrightnessSetting = true;
+ }
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
}
+ if (updateScreenBrightnessSetting) {
+ // Tell the rest of the system about the new brightness in case we had to change it
+ // for things like auto-brightness or high-brightness-mode. Note that we do this
+ // before applying the low power or dim transformations so that the slider
+ // accurately represents the full possible range, even if they range changes what
+ // it means in absolute terms.
+ putScreenBrightnessSetting(brightnessState);
+ }
+
// Apply dimming by at least some minimum amount when user activity
// timeout is about to expire.
if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
@@ -1208,9 +1230,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// animate to. To avoid this, we check the value first.
// If the brightnessState is off (-1.0f) we still want to animate to the minimum
// brightness (0.0f) to accommodate for LED displays, which can appear bright to the
- // user even when the display is all black.
- float animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT
- ? PowerManager.BRIGHTNESS_MIN : brightnessState;
+ // user even when the display is all black. We also clamp here in case some
+ // transformations to the brightness have pushed it outside of the currently
+ // allowed range.
+ float animateValue = clampScreenBrightness(brightnessState);
final float currentBrightness = mPowerState.getScreenBrightness();
if (isValidBrightnessValue(animateValue)
&& !BrightnessSynchronizer.floatEquals(animateValue, currentBrightness)) {
@@ -1353,6 +1376,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
msg.sendToTarget();
}
+ private HighBrightnessModeController createHbmController() {
+ final DisplayDeviceConfig ddConfig =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+ final DisplayDeviceConfig.HighBrightnessModeData hbmData =
+ ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
+ return new HighBrightnessModeController(mHandler, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX, hbmData, () -> sendUpdatePowerStateLocked());
+ }
+
private void blockScreenOn() {
if (mPendingScreenOnUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -1468,10 +1500,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private float clampScreenBrightness(float value) {
if (Float.isNaN(value)) {
- return PowerManager.BRIGHTNESS_MIN;
+ value = PowerManager.BRIGHTNESS_MIN;
}
- return MathUtils.constrain(
- value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+ return MathUtils.constrain(value,
+ mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
}
// Checks whether the brightness is within the valid brightness range, not including the off or
@@ -1868,7 +1900,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
: 1.0f;
mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
powerFactor, hadUserDataPoint,
- mAutomaticBrightnessController.isDefaultConfig());
+ mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId);
}
}
@@ -2036,11 +2068,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessController.dump(pw);
}
- if (mBrightnessTracker != null) {
- pw.println();
- mBrightnessTracker.dump(pw);
- }
-
pw.println();
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.dump(pw);
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 12b810f906f2..2e5561dc0aea 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,7 +37,7 @@ import java.util.LinkedList;
class HighBrightnessModeController {
private static final String TAG = "HighBrightnessModeController";
- private static final boolean DEBUG_HBM = false;
+ private static final boolean DEBUG = false;
private final float mBrightnessMin;
private final float mBrightnessMax;
@@ -48,6 +48,7 @@ class HighBrightnessModeController {
private boolean mIsInAllowedAmbientRange = false;
private boolean mIsTimeAvailable = false;
+ private boolean mIsAutoBrightnessEnabled = false;
private float mAutoBrightness;
/**
@@ -84,6 +85,17 @@ class HighBrightnessModeController {
};
}
+ void setAutoBrightnessEnabled(boolean isEnabled) {
+ if (isEnabled == mIsAutoBrightnessEnabled) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "setAutoBrightness( " + isEnabled + " )");
+ }
+ mIsAutoBrightnessEnabled = isEnabled;
+ mIsInAllowedAmbientRange = false; // reset when auto-brightness switches
+ }
+
float getCurrentBrightnessMin() {
return mBrightnessMin;
}
@@ -102,7 +114,7 @@ class HighBrightnessModeController {
}
void onAmbientLuxChange(float ambientLux) {
- if (!deviceSupportsHbm()) {
+ if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) {
return;
}
@@ -132,7 +144,7 @@ class HighBrightnessModeController {
mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
mRunningStartTimeMillis = -1;
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "New HBM event: " + mEvents.getFirst());
}
}
@@ -142,7 +154,7 @@ class HighBrightnessModeController {
}
private boolean isCurrentlyAllowed() {
- return mIsTimeAvailable && mIsInAllowedAmbientRange;
+ return mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange;
}
private boolean deviceSupportsHbm() {
@@ -167,7 +179,7 @@ class HighBrightnessModeController {
timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
}
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "Time already used after current session: " + timeAlreadyUsed);
}
@@ -187,7 +199,7 @@ class HighBrightnessModeController {
timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
}
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed);
}
@@ -220,7 +232,7 @@ class HighBrightnessModeController {
nextTimeout = timeWhenMinIsGainedBack;
}
- if (DEBUG_HBM) {
+ if (DEBUG) {
Slog.d(TAG, "HBM recalculated. IsAllowedWithoutRestrictions: "
+ isAllowedWithoutRestrictions
+ ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d88896c01e4b..2546118f1cc7 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -682,7 +682,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|| oldState == Display.STATE_ON_SUSPEND) {
setDisplayState(Display.STATE_ON);
currentState = Display.STATE_ON;
- } else {
+
+ // If UNKNOWN, we still want to set the initial display state,
+ // otherwise, return early.
+ } else if (oldState != Display.STATE_UNKNOWN) {
return; // old state and new state is off
}
}
@@ -850,14 +853,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// Do not lock when calling these SurfaceControl methods because they are sync
// operations that may block for a while when setting display power mode.
mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
-
- final int sfActiveModeId = mSurfaceControlProxy
- .getDynamicDisplayInfo(displayToken).activeDisplayModeId;
- synchronized (getSyncRoot()) {
- if (updateActiveModeLocked(sfActiveModeId)) {
- updateDeviceInfoLocked();
- }
- }
}
@Override
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index d6826be248df..fcfa674dcc4e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -357,7 +357,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
final int groupId = mDisplayGroups.keyAt(i);
final DisplayGroup group = mDisplayGroups.valueAt(i);
- final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) < 0;
+ final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) > -1;
final int changeCount = group.getChangeCountLocked();
if (group.isEmptyLocked()) {
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index d2baaf2228a1..0ba191c0762f 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -91,6 +91,10 @@ final class WifiDisplayAdapter extends DisplayAdapter {
private boolean mPendingStatusChangeBroadcast;
+ private static final String[] RECEIVER_PERMISSIONS_FOR_BROADCAST = {
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ };
+
// Called with SyncRoot lock held.
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
@@ -432,7 +436,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
}
// Send protected broadcast about wifi display status to registered receivers.
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ getContext().createContextAsUser(UserHandle.ALL, 0)
+ .sendBroadcastWithMultiplePermissions(intent, RECEIVER_PERMISSIONS_FOR_BROADCAST);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 900ec905609f..2b7d2074a7e0 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -31,17 +31,18 @@ import android.os.SharedMemory;
import android.os.ShellCallback;
import android.system.ErrnoException;
import android.text.FontConfig;
+import android.text.TextUtils;
import android.util.AndroidException;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.graphics.fonts.IFontManager;
+import com.android.internal.security.VerityUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.security.VerityUtils;
import java.io.File;
import java.io.FileDescriptor;
@@ -66,6 +67,8 @@ public final class FontManagerService extends IFontManager.Stub {
@Override
public FontConfig getFontConfig() {
+ getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
+ "UPDATE_FONTS permission required.");
return getSystemFontConfig();
}
@@ -148,10 +151,24 @@ public final class FontManagerService extends IFontManager.Stub {
/* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
@Override
- public String getPostScriptName(File file) throws IOException {
+ public String getCanonicalFileName(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
- return FontFileUtil.getPostScriptName(buffer, 0);
+ String psName = FontFileUtil.getPostScriptName(buffer, 0);
+ int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
+ int isCollection = FontFileUtil.isCollectionFont(buffer);
+
+ if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
+ return null;
+ }
+
+ String extension;
+ if (isCollection == 1) {
+ extension = isType1Font == 1 ? ".otc" : ".ttc";
+ } else {
+ extension = isType1Font == 1 ? ".otf" : ".ttf";
+ }
+ return psName + extension;
} finally {
NioUtils.freeDirectBuffer(buffer);
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 86dbe86f85ee..4f95d27a085e 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -56,14 +56,12 @@ final class UpdatableFontDir {
private static final String TAG = "UpdatableFontDir";
private static final String RANDOM_DIR_PREFIX = "~~";
- // TODO: Support .otf
- private static final String ALLOWED_EXTENSION = ".ttf";
private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
/** Interface to mock font file access in tests. */
interface FontFileParser {
- String getPostScriptName(File file) throws IOException;
+ String getCanonicalFileName(File file) throws IOException;
long getRevision(File file) throws IOException;
}
@@ -321,20 +319,20 @@ final class UpdatableFontDir {
FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
"Failed to setup fs-verity.", e);
}
- String postScriptName;
+ String canonicalFileName;
try {
- postScriptName = mParser.getPostScriptName(tempNewFontFile);
+ canonicalFileName = mParser.getCanonicalFileName(tempNewFontFile);
} catch (IOException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_INVALID_FONT_FILE,
"Failed to read PostScript name from font file", e);
}
- if (postScriptName == null) {
+ if (canonicalFileName == null) {
throw new SystemFontException(
FontManager.RESULT_ERROR_INVALID_FONT_NAME,
"Failed to read PostScript name from font file");
}
- File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+ File newFontFile = new File(newDir, canonicalFileName);
if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
@@ -380,20 +378,38 @@ final class UpdatableFontDir {
return dir;
}
+ private FontFileInfo lookupFontFileInfo(File file) {
+ String name = file.getName();
+
+ if (!name.endsWith(".ttf") && !name.endsWith(".otf") && !name.endsWith(".ttc")
+ && !name.endsWith(".otc")) {
+ return null;
+ }
+ String key = name.substring(0, name.length() - 4);
+ return mFontFileInfoMap.get(key);
+ }
+
+ private void putFontFileInfo(FontFileInfo info) {
+ String name = info.getFile().getName();
+ // The file name in FontFileInfo is already validated. Thus, just strip last 4 chars.
+ String key = name.substring(0, name.length() - 4);
+ mFontFileInfoMap.put(key, info);
+ }
+
/**
* Add the given {@link FontFileInfo} to {@link #mFontFileInfoMap} if its font revision is
* higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
* #mPreinstalledFontDirs}).
*/
private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
- String name = fontFileInfo.getFile().getName();
- FontFileInfo existingInfo = mFontFileInfoMap.get(name);
+ FontFileInfo existingInfo = lookupFontFileInfo(fontFileInfo.getFile());
final boolean shouldAddToMap;
if (existingInfo == null) {
// We got a new updatable font. We need to check if it's newer than preinstalled fonts.
// Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font
// with 'name'.
- shouldAddToMap = getPreinstalledFontRevision(name) < fontFileInfo.getRevision();
+ long preInstalledRev = getPreinstalledFontRevision(fontFileInfo.getFile().getName());
+ shouldAddToMap = preInstalledRev < fontFileInfo.getRevision();
} else {
shouldAddToMap = existingInfo.getRevision() < fontFileInfo.getRevision();
}
@@ -401,7 +417,7 @@ final class UpdatableFontDir {
if (deleteOldFile && existingInfo != null) {
FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir());
}
- mFontFileInfoMap.put(name, fontFileInfo);
+ putFontFileInfo(fontFileInfo);
} else {
if (deleteOldFile) {
FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir());
@@ -464,15 +480,18 @@ final class UpdatableFontDir {
*/
private boolean validateFontFileName(File file) {
String fileName = file.getName();
- String postScriptName = getPostScriptName(file);
- return (postScriptName + ALLOWED_EXTENSION).equals(fileName);
+ String canonicalFileName = getCanonicalFileName(file);
+ if (canonicalFileName == null) {
+ return false;
+ }
+ return canonicalFileName.equals(fileName);
}
/** Returns the PostScript name of the given font file, or null. */
@Nullable
- private String getPostScriptName(File file) {
+ private String getCanonicalFileName(File file) {
try {
- return mParser.getPostScriptName(file);
+ return mParser.getCanonicalFileName(file);
} catch (IOException e) {
Slog.e(TAG, "Failed to read font file", e);
return null;
@@ -514,7 +533,7 @@ final class UpdatableFontDir {
List<FontConfig.Font> fontList = fontFamily.getFontList();
for (int i = 0; i < fontList.size(); i++) {
FontConfig.Font font = fontList.get(i);
- FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName());
+ FontFileInfo info = lookupFontFileInfo(font.getFile());
if (info == null) {
return null;
}
@@ -537,8 +556,9 @@ final class UpdatableFontDir {
Map<String, File> getFontFileMap() {
Map<String, File> map = new ArrayMap<>();
- for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
- map.put(entry.getKey(), entry.getValue().getFile());
+ for (int i = 0; i < mFontFileInfoMap.size(); ++i) {
+ File file = mFontFileInfoMap.valueAt(i).getFile();
+ map.put(file.getName(), file);
}
return map;
}
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index c23e2e691b55..f6828d129728 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -97,16 +97,25 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
@Override
public boolean start() {
- // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
- // The message is re-sent at the end of the action for devices that don't support 2.0.
- sendSetStreamPath();
- int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
- .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus();
- if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
+ // The message is re-sent at the end of the action for devices that don't support 2.0.
+ sendSetStreamPath();
+
+ if (!mIsCec20) {
queryDevicePowerStatus();
- } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- return true;
+ } else {
+ int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+ HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork()
+ .getCecDeviceInfo(getTargetAddress());
+ if (targetDevice != null) {
+ targetPowerStatus = targetDevice.getDevicePowerStatus();
+ }
+ if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ queryDevicePowerStatus();
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 624af30854dc..6fbb26c4a5ee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -35,33 +35,19 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.util.ArrayMap;
-import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ConcurrentUtils;
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.Setting;
-import com.android.server.hdmi.cec.config.Value;
-import com.android.server.hdmi.cec.config.XmlParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
-import java.util.Set;
import java.util.concurrent.Executor;
-import javax.xml.datatype.DatatypeConfigurationException;
-
/**
* The {@link HdmiCecConfig} class is used for getting information about
* available HDMI CEC settings.
@@ -74,6 +60,10 @@ public class HdmiCecConfig {
private static final String SHARED_PREFS_DIR = "shared_prefs";
private static final String SHARED_PREFS_NAME = "cec_config.xml";
+ private static final int STORAGE_SYSPROPS = 0;
+ private static final int STORAGE_GLOBAL_SETTINGS = 1;
+ private static final int STORAGE_SHARED_PREFS = 2;
+
@IntDef({
STORAGE_SYSPROPS,
STORAGE_GLOBAL_SETTINGS,
@@ -81,10 +71,6 @@ public class HdmiCecConfig {
})
private @interface Storage {}
- private static final int STORAGE_SYSPROPS = 0;
- private static final int STORAGE_GLOBAL_SETTINGS = 1;
- private static final int STORAGE_SHARED_PREFS = 2;
-
private static final String VALUE_TYPE_STRING = "string";
private static final String VALUE_TYPE_INT = "int";
@@ -96,8 +82,6 @@ public class HdmiCecConfig {
@NonNull private final Context mContext;
@NonNull private final StorageAdapter mStorageAdapter;
- @Nullable private final CecSettings mSystemConfig;
- @Nullable private final CecSettings mVendorOverride;
private final Object mLock = new Object();
@@ -107,6 +91,18 @@ public class HdmiCecConfig {
private SettingsObserver mSettingsObserver;
+ private LinkedHashMap<String, Setting> mSettings = new LinkedHashMap<>();
+
+ /**
+ * Exception thrown when the CEC Configuration setup verification fails.
+ * This usually means a settings lacks default value or storage/storage key.
+ */
+ public static class VerificationException extends RuntimeException {
+ public VerificationException(String message) {
+ super(message);
+ }
+ }
+
/**
* Listener used to get notifications when value of a setting changes.
*/
@@ -202,91 +198,297 @@ public class HdmiCecConfig {
}
}
- @VisibleForTesting
- HdmiCecConfig(@NonNull Context context,
- @NonNull StorageAdapter storageAdapter,
- @Nullable CecSettings systemConfig,
- @Nullable CecSettings vendorOverride) {
- mContext = context;
- mStorageAdapter = storageAdapter;
- mSystemConfig = systemConfig;
- mVendorOverride = vendorOverride;
- if (mSystemConfig == null) {
- Slog.i(TAG, "CEC system configuration XML missing.");
+ private class Value {
+ private final String mStringValue;
+ private final Integer mIntValue;
+
+ Value(@NonNull String value) {
+ mStringValue = value;
+ mIntValue = null;
}
- if (mVendorOverride == null) {
- Slog.i(TAG, "CEC OEM configuration override XML missing.");
+
+ Value(@NonNull Integer value) {
+ mStringValue = null;
+ mIntValue = value;
}
- }
- HdmiCecConfig(@NonNull Context context) {
- this(context, new StorageAdapter(context),
- readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(),
- ETC_DIR, CONFIG_FILE)),
- readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
- ETC_DIR, CONFIG_FILE)));
+ String getStringValue() {
+ return mStringValue;
+ }
+
+ Integer getIntValue() {
+ return mIntValue;
+ }
}
- @Nullable
- private static CecSettings readSettingsFromFile(@NonNull File file) {
- if (!file.exists()) {
- return null;
+ private class Setting {
+ @NonNull private final Context mContext;
+ @NonNull private final @CecSettingName String mName;
+ private final boolean mUserConfigurable;
+
+ private Value mDefaultValue = null;
+ private List<Value> mAllowedValues = new ArrayList<>();
+
+ Setting(@NonNull Context context,
+ @NonNull @CecSettingName String name,
+ int userConfResId) {
+ mContext = context;
+ mName = name;
+ mUserConfigurable = mContext.getResources().getBoolean(userConfResId);
}
- if (!file.isFile()) {
- Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
- return null;
+
+ public @CecSettingName String getName() {
+ return mName;
}
- try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
- return XmlParser.read(in);
- } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
- Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
+
+ public @ValueType String getValueType() {
+ return getDefaultValue().getStringValue() != null
+ ? VALUE_TYPE_STRING
+ : VALUE_TYPE_INT;
}
- return null;
- }
- @NonNull
- @VisibleForTesting
- static HdmiCecConfig createFromStrings(@NonNull Context context,
- @NonNull StorageAdapter storageAdapter,
- @Nullable String productConfigXml,
- @Nullable String vendorOverrideXml) {
- CecSettings productConfig = null;
- CecSettings vendorOverride = null;
- try {
- if (productConfigXml != null) {
- productConfig = XmlParser.read(
- new ByteArrayInputStream(productConfigXml.getBytes()));
- }
- if (vendorOverrideXml != null) {
- vendorOverride = XmlParser.read(
- new ByteArrayInputStream(vendorOverrideXml.getBytes()));
+ public Value getDefaultValue() {
+ if (mDefaultValue == null) {
+ throw new VerificationException("Invalid CEC setup for '"
+ + this.getName() + "' setting. "
+ + "Setting has no default value.");
}
- } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
- Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+ return mDefaultValue;
}
- return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride);
- }
- @Nullable
- private Setting getSetting(@NonNull String name) {
- if (mSystemConfig == null) {
- return null;
- }
- if (mVendorOverride != null) {
- // First read from the vendor override.
- for (Setting setting : mVendorOverride.getSetting()) {
- if (setting.getName().equals(name)) {
- return setting;
+ public boolean getUserConfigurable() {
+ return mUserConfigurable;
+ }
+
+ private void registerValue(@NonNull Value value,
+ int allowedResId, int defaultResId) {
+ if (mContext.getResources().getBoolean(allowedResId)) {
+ mAllowedValues.add(value);
+ if (mContext.getResources().getBoolean(defaultResId)) {
+ if (mDefaultValue != null) {
+ throw new VerificationException("Invalid CEC setup for '"
+ + this.getName() + "' setting. "
+ + "Setting already has a default value.");
+ }
+ mDefaultValue = value;
}
}
}
- // If not found, try the system config.
- for (Setting setting : mSystemConfig.getSetting()) {
- if (setting.getName().equals(name)) {
- return setting;
- }
+
+ public void registerValue(@NonNull String value, int allowedResId,
+ int defaultResId) {
+ registerValue(new Value(value), allowedResId, defaultResId);
}
- return null;
+
+ public void registerValue(int value, int allowedResId,
+ int defaultResId) {
+ registerValue(new Value(value), allowedResId, defaultResId);
+ }
+
+
+ public List<Value> getAllowedValues() {
+ return mAllowedValues;
+ }
+ }
+
+ @VisibleForTesting
+ HdmiCecConfig(@NonNull Context context,
+ @NonNull StorageAdapter storageAdapter) {
+ mContext = context;
+ mStorageAdapter = storageAdapter;
+
+ Setting hdmiCecEnabled = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ R.bool.config_cecHdmiCecEnabled_userConfigurable);
+ hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED,
+ R.bool.config_cecHdmiCecControlEnabled_allowed,
+ R.bool.config_cecHdmiCecControlEnabled_default);
+ hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
+ R.bool.config_cecHdmiCecControlDisabled_allowed,
+ R.bool.config_cecHdmiCecControlDisabled_default);
+
+ Setting hdmiCecVersion = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ R.bool.config_cecHdmiCecVersion_userConfigurable);
+ hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+ R.bool.config_cecHdmiCecVersion14b_allowed,
+ R.bool.config_cecHdmiCecVersion14b_default);
+ hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_2_0,
+ R.bool.config_cecHdmiCecVersion20_allowed,
+ R.bool.config_cecHdmiCecVersion20_default);
+
+ Setting powerControlMode = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ R.bool.config_cecSendStandbyOnSleep_userConfigurable);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ R.bool.config_cecPowerControlModeTv_allowed,
+ R.bool.config_cecPowerControlModeTv_default);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
+ R.bool.config_cecPowerControlModeBroadcast_allowed,
+ R.bool.config_cecPowerControlModeBroadcast_default);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE,
+ R.bool.config_cecPowerControlModeNone_allowed,
+ R.bool.config_cecPowerControlModeNone_default);
+
+ Setting powerStateChangeOnActiveSourceLost = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
+ powerStateChangeOnActiveSourceLost.registerValue(
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed,
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
+ powerStateChangeOnActiveSourceLost.registerValue(
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW,
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+
+ Setting systemAudioModeMuting = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ R.bool.config_cecSystemAudioModeMuting_userConfigurable);
+ systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+ R.bool.config_cecSystemAudioModeMutingEnabled_allowed,
+ R.bool.config_cecSystemAudioModeMutingEnabled_default);
+ systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED,
+ R.bool.config_cecSystemAudioModeMutingDisabled_allowed,
+ R.bool.config_cecSystemAudioModeMutingDisabled_default);
+
+ Setting volumeControlMode = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ R.bool.config_cecVolumeControlMode_userConfigurable);
+ volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_ENABLED,
+ R.bool.config_cecVolumeControlModeEnabled_allowed,
+ R.bool.config_cecVolumeControlModeEnabled_default);
+ volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_DISABLED,
+ R.bool.config_cecVolumeControlModeDisabled_allowed,
+ R.bool.config_cecVolumeControlModeDisabled_default);
+
+ Setting tvWakeOnOneTouchPlay = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
+ tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED,
+ R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed,
+ R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
+ tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED,
+ R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed,
+ R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
+
+ Setting tvSendStandbyOnSleep = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
+ tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED,
+ R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed,
+ R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
+ tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_DISABLED,
+ R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed,
+ R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+
+ Setting rcProfileTv = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+ R.bool.config_cecRcProfileTv_userConfigurable);
+ rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_NONE,
+ R.bool.config_cecRcProfileTvNone_allowed,
+ R.bool.config_cecRcProfileTvNone_default);
+ rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_ONE,
+ R.bool.config_cecRcProfileTvOne_allowed,
+ R.bool.config_cecRcProfileTvOne_default);
+ rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_TWO,
+ R.bool.config_cecRcProfileTvTwo_allowed,
+ R.bool.config_cecRcProfileTvTwo_default);
+ rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_THREE,
+ R.bool.config_cecRcProfileTvThree_allowed,
+ R.bool.config_cecRcProfileTvThree_default);
+ rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_FOUR,
+ R.bool.config_cecRcProfileTvFour_allowed,
+ R.bool.config_cecRcProfileTvFour_default);
+
+ Setting rcProfileSourceRootMenu = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+ R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
+ rcProfileSourceRootMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_HANDLED,
+ R.bool.config_cecRcProfileSourceRootMenuHandled_allowed,
+ R.bool.config_cecRcProfileSourceRootMenuHandled_default);
+ rcProfileSourceRootMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED,
+ R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed,
+ R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
+
+ Setting rcProfileSourceSetupMenu = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+ R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
+ rcProfileSourceSetupMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_HANDLED,
+ R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed,
+ R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
+ rcProfileSourceSetupMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED,
+ R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed,
+ R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
+
+ Setting rcProfileSourceContentsMenu = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+ R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
+ rcProfileSourceContentsMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED,
+ R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed,
+ R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
+ rcProfileSourceContentsMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED,
+ R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed,
+ R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
+
+ Setting rcProfileSourceTopMenu = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+ R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
+ rcProfileSourceTopMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_HANDLED,
+ R.bool.config_cecRcProfileSourceTopMenuHandled_allowed,
+ R.bool.config_cecRcProfileSourceTopMenuHandled_default);
+ rcProfileSourceTopMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED,
+ R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed,
+ R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
+
+ Setting rcProfileSourceMediaContextSensitiveMenu = registerSetting(
+ HdmiControlManager
+ .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
+ rcProfileSourceMediaContextSensitiveMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED,
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed,
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
+ rcProfileSourceMediaContextSensitiveMenu.registerValue(
+ HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED,
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed,
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
+
+ verifySettings();
+ }
+
+ HdmiCecConfig(@NonNull Context context) {
+ this(context, new StorageAdapter(context));
+ }
+
+ private Setting registerSetting(@NonNull @CecSettingName String name,
+ int userConfResId) {
+ Setting setting = new Setting(mContext, name, userConfResId);
+ mSettings.put(name, setting);
+ return setting;
+ }
+
+ private void verifySettings() {
+ for (Setting setting: mSettings.values()) {
+ // This will throw an exception when a setting
+ // doesn't have a default value assigned.
+ setting.getDefaultValue();
+ getStorage(setting);
+ getStorageKey(setting);
+ }
+ }
+
+ @Nullable
+ private Setting getSetting(@NonNull String name) {
+ return mSettings.containsKey(name) ? mSettings.get(name) : null;
}
@Storage
@@ -322,7 +524,7 @@ public class HdmiCecConfig {
.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
return STORAGE_SHARED_PREFS;
default:
- throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+ throw new VerificationException("Invalid CEC setting '" + setting.getName()
+ "' storage.");
}
}
@@ -359,7 +561,7 @@ public class HdmiCecConfig {
.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
return setting.getName();
default:
- throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+ throw new VerificationException("Invalid CEC setting '" + setting.getName()
+ "' storage key.");
}
}
@@ -396,10 +598,6 @@ public class HdmiCecConfig {
}
}
- private int getIntValue(@NonNull Value value) {
- return Integer.decode(value.getIntValue());
- }
-
private void notifyGlobalSettingChanged(String setting) {
switch (setting) {
case Global.HDMI_CONTROL_ENABLED:
@@ -533,41 +731,20 @@ public class HdmiCecConfig {
* Returns a list of all settings based on the XML metadata.
*/
public @CecSettingName List<String> getAllSettings() {
- if (mSystemConfig == null) {
- return new ArrayList<String>();
- }
- List<String> allSettings = new ArrayList<String>();
- for (Setting setting : mSystemConfig.getSetting()) {
- allSettings.add(setting.getName());
- }
- return allSettings;
+ return new ArrayList<>(mSettings.keySet());
}
/**
* Returns a list of user-modifiable settings based on the XML metadata.
*/
public @CecSettingName List<String> getUserSettings() {
- if (mSystemConfig == null) {
- return new ArrayList<String>();
- }
- Set<String> userSettings = new HashSet<String>();
- // First read from the system config.
- for (Setting setting : mSystemConfig.getSetting()) {
+ List<String> settings = new ArrayList<>();
+ for (Setting setting: mSettings.values()) {
if (setting.getUserConfigurable()) {
- userSettings.add(setting.getName());
- }
- }
- if (mVendorOverride != null) {
- // Next either add or remove based on the vendor override.
- for (Setting setting : mVendorOverride.getSetting()) {
- if (setting.getUserConfigurable()) {
- userSettings.add(setting.getName());
- } else {
- userSettings.remove(setting.getName());
- }
+ settings.add(setting.getName());
}
}
- return new ArrayList(userSettings);
+ return settings;
}
/**
@@ -607,7 +784,7 @@ public class HdmiCecConfig {
+ "' is not a string-type setting.");
}
List<String> allowedValues = new ArrayList<String>();
- for (Value allowedValue : setting.getAllowedValues().getValue()) {
+ for (Value allowedValue : setting.getAllowedValues()) {
allowedValues.add(allowedValue.getStringValue());
}
return allowedValues;
@@ -626,8 +803,8 @@ public class HdmiCecConfig {
+ "' is not a string-type setting.");
}
List<Integer> allowedValues = new ArrayList<Integer>();
- for (Value allowedValue : setting.getAllowedValues().getValue()) {
- allowedValues.add(getIntValue(allowedValue));
+ for (Value allowedValue : setting.getAllowedValues()) {
+ allowedValues.add(allowedValue.getIntValue());
}
return allowedValues;
}
@@ -659,7 +836,7 @@ public class HdmiCecConfig {
throw new IllegalArgumentException("Setting '" + name
+ "' is not a string-type setting.");
}
- return getIntValue(getSetting(name).getDefaultValue());
+ return getSetting(name).getDefaultValue().getIntValue();
}
/**
@@ -691,7 +868,7 @@ public class HdmiCecConfig {
+ "' is not a int-type setting.");
}
HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
- String defaultValue = Integer.toString(getIntValue(setting.getDefaultValue()));
+ String defaultValue = Integer.toString(setting.getDefaultValue().getIntValue());
String value = retrieveValue(setting, defaultValue);
return Integer.parseInt(value);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index d8914b389191..bdc4e66cf7f9 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -500,7 +500,8 @@ abstract class HdmiCecLocalDevice {
HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
// If no non-default display name is available for the device, request the devices OSD name.
- if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) {
+ if (cecDeviceInfo != null && cecDeviceInfo.getDisplayName().equals(
+ HdmiUtils.getDefaultDeviceName(address))) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7235a921254d..90d64339eac0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -30,6 +30,7 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL;
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
+import android.annotation.Nullable;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -1147,6 +1148,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
&& getAvrDeviceInfo() != null;
}
+ @Nullable
@ServiceThreadOnly
HdmiDeviceInfo getAvrDeviceInfo() {
assertRunOnServiceThread();
@@ -1157,6 +1159,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return getSafeAvrDeviceInfo() != null;
}
+ @Nullable
HdmiDeviceInfo getSafeAvrDeviceInfo() {
return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index b748ae026cfc..7ceaa959212e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -185,6 +185,12 @@ public class HdmiCecNetwork {
mLocalDevices.clear();
}
+ /**
+ * Get the device info of a local device or a device in the CEC network by a device id.
+ * @param id id of the device to get
+ * @return the device with the given id, or {@code null}
+ */
+ @Nullable
public HdmiDeviceInfo getDeviceInfo(int id) {
return mDeviceInfos.get(id);
}
@@ -717,6 +723,7 @@ public class HdmiCecNetwork {
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
+ @Nullable
HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 115cafedca93..03a83380246f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -431,6 +431,13 @@ public class HdmiControlService extends SystemService {
private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();
+ @VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes) {
+ super(context);
+ mLocalDevices = deviceTypes;
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mHdmiCecConfig = new HdmiCecConfig(context);
+ }
+
public HdmiControlService(Context context) {
super(context);
List<Integer> deviceTypes = HdmiProperties.device_type();
@@ -988,6 +995,7 @@ public class HdmiControlService extends SystemService {
return mCecController.getVendorId();
}
+ @Nullable
@ServiceThreadOnly
HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 02b09b15b464..979e7a452e43 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -16,10 +16,12 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.hdmi.IHdmiControlCallback;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
/**
* Feature action that performs one touch play against TV/Display device. This action is initiated
@@ -40,13 +42,15 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
// standby mode, and do not accept the command until their power status becomes 'ON'.
// For a workaround, we send <Give Device Power Status> commands periodically to make sure
// the device switches its status to 'ON'. Then we send additional <Active Source>.
- private static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;
+ @VisibleForTesting
+ static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;
// The maximum number of times we send <Give Device Power Status> before we give up.
// We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
private static final int LOOP_COUNTER_MAX = 10;
private final int mTargetAddress;
+ private final boolean mIsCec20;
private int mPowerStatusCounter = 0;
@@ -59,14 +63,24 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new OneTouchPlayAction(source, targetAddress,
- callback);
+ return new OneTouchPlayAction(source, targetAddress, callback);
}
private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
IHdmiControlCallback callback) {
+ this(localDevice, targetAddress, callback,
+ localDevice.getDeviceInfo().getCecVersion()
+ >= HdmiControlManager.HDMI_CEC_VERSION_2_0
+ && getTargetCecVersion(localDevice, targetAddress)
+ >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ }
+
+ @VisibleForTesting
+ OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+ IHdmiControlCallback callback, boolean isCec20) {
super(localDevice, callback);
mTargetAddress = targetAddress;
+ mIsCec20 = isCec20;
}
@Override
@@ -74,6 +88,9 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
// Because only source device can create this action, it's safe to cast.
mSource = source();
sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
+
+ boolean is20TargetOnBefore = mIsCec20 && getTargetDevicePowerStatus(mSource, mTargetAddress,
+ HdmiControlManager.POWER_STATUS_UNKNOWN) == HdmiControlManager.POWER_STATUS_ON;
broadcastActiveSource();
// If the device is not an audio system itself, request the connected audio system to
// turn on.
@@ -81,7 +98,25 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(),
Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true));
}
- queryDevicePowerStatus();
+
+ if (!mIsCec20) {
+ queryDevicePowerStatus();
+ } else {
+ int targetPowerStatus = getTargetDevicePowerStatus(mSource, mTargetAddress,
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ queryDevicePowerStatus();
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+ if (!is20TargetOnBefore) {
+ // Suppress 2nd <Active Source> message if the target device was already on when
+ // the 1st one was sent.
+ broadcastActiveSource();
+ }
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
+ }
+ mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
@@ -101,7 +136,6 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
}
private void queryDevicePowerStatus() {
- mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
mTargetAddress));
}
@@ -150,4 +184,23 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
+ private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
+ int targetLogicalAddress) {
+ HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+ targetLogicalAddress);
+ if (targetDevice != null) {
+ return targetDevice.getCecVersion();
+ }
+ return HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+ }
+
+ private static int getTargetDevicePowerStatus(HdmiCecLocalDevice localDevice,
+ int targetLogicalAddress, int defaultPowerStatus) {
+ HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+ targetLogicalAddress);
+ if (targetDevice != null) {
+ return targetDevice.getDevicePowerStatus();
+ }
+ return defaultPowerStatus;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index f7e871d0b645..56e538b1abfa 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -17,6 +17,8 @@
package com.android.server.hdmi;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
/**
@@ -30,6 +32,14 @@ final class SystemAudioAutoInitiationAction extends HdmiCecFeatureAction {
// <Give System Audio Mode Status> to AV Receiver.
private static final int STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS = 1;
+ @VisibleForTesting
+ static final int RETRIES_ON_TIMEOUT = 1;
+
+ // On some audio devices the <System Audio Mode Status> message can be delayed as the device
+ // is just waking up. Retry the <Give System Audio Mode Status> message to ensure we properly
+ // initialize system audio.
+ private int mRetriesOnTimeOut = RETRIES_ON_TIMEOUT;
+
SystemAudioAutoInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
super(source);
mAvrAddress = avrAddress;
@@ -100,6 +110,13 @@ final class SystemAudioAutoInitiationAction extends HdmiCecFeatureAction {
switch (mState) {
case STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS:
+ if (mRetriesOnTimeOut > 0) {
+ mRetriesOnTimeOut--;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ sendGiveSystemAudioModeStatus();
+ return;
+ }
+
handleSystemAudioModeStatusTimeout();
break;
}
diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml
deleted file mode 100644
index 191e725181ca..000000000000
--- a/services/core/java/com/android/server/hdmi/cec_config.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<cec-settings>
- <setting name="hdmi_cec_enabled"
- value-type="int"
- user-configurable="true">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="hdmi_cec_version"
- value-type="int"
- user-configurable="true">
- <allowed-values>
- <value int-value="0x05" />
- <value int-value="0x06" />
- </allowed-values>
- <default-value int-value="0x05" />
- </setting>
- <setting name="send_standby_on_sleep"
- value-type="string"
- user-configurable="true">
- <allowed-values>
- <value string-value="to_tv" />
- <value string-value="broadcast" />
- <value string-value="none" />
- </allowed-values>
- <default-value string-value="to_tv" />
- </setting>
- <setting name="power_state_change_on_active_source_lost"
- value-type="string"
- user-configurable="true">
- <allowed-values>
- <value string-value="none" />
- <value string-value="standby_now" />
- </allowed-values>
- <default-value string-value="none" />
- </setting>
- <setting name="system_audio_mode_muting"
- value-type="int"
- user-configurable="true">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="volume_control_enabled"
- value-type="int"
- user-configurable="true">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="tv_wake_on_one_touch_play"
- value-type="int"
- user-configurable="true">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="tv_send_standby_on_sleep"
- value-type="int"
- user-configurable="true">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="rc_profile_tv"
- value-type="int"
- user-configurable="false">
- <allowed-values>
- <value int-value="0x0" />
- <value int-value="0x2" />
- <value int-value="0x6" />
- <value int-value="0xA" />
- <value int-value="0xE" />
- </allowed-values>
- <default-value int-value="0x0" />
- </setting>
- <setting name="rc_profile_source_handles_root_menu"
- value-type="int"
- user-configurable="false">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="rc_profile_source_handles_setup_menu"
- value-type="int"
- user-configurable="false">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="1" />
- </setting>
- <setting name="rc_profile_source_handles_contents_menu"
- value-type="int"
- user-configurable="false">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="0" />
- </setting>
- <setting name="rc_profile_source_handles_top_menu"
- value-type="int"
- user-configurable="false">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="0" />
- </setting>
- <setting name="rc_profile_source_handles_media_context_sensitive_menu"
- value-type="int"
- user-configurable="false">
- <allowed-values>
- <value int-value="0" />
- <value int-value="1" />
- </allowed-values>
- <default-value int-value="0" />
- </setting>
-</cec-settings>
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index edb5d97f1a5a..cbe6e69cbef3 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -304,7 +304,7 @@ public class InputManagerService extends IInputManager.Stub
int displayId, InputApplicationHandle application);
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
- IBinder fromChannelToken, IBinder toChannelToken);
+ IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -586,18 +586,13 @@ public class InputManagerService extends IInputManager.Stub
private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
- // Remove all viewport operations. They will be built-into the window transforms.
+ // Remove display projection information from DisplayViewport, leaving only the
+ // orientation. The display projection will be built-into the window transforms.
for (int i = viewports.size() - 1; i >= 0; --i) {
final DisplayViewport v = vArray[i] = viewports.get(i).makeCopy();
- // deviceWidth/Height are apparently in "rotated" space, so flip them if needed.
- if (v.orientation % 2 != 0) {
- final int dw = v.deviceWidth;
- v.deviceWidth = v.deviceHeight;
- v.deviceHeight = dw;
- }
+ // Note: the deviceWidth/Height are in rotated with the orientation.
v.logicalFrame.set(0, 0, v.deviceWidth, v.deviceHeight);
v.physicalFrame.set(0, 0, v.deviceWidth, v.deviceHeight);
- v.orientation = 0;
}
} else {
for (int i = viewports.size() - 1; i >= 0; --i) {
@@ -1732,12 +1727,14 @@ public class InputManagerService extends IInputManager.Stub
* @param fromChannel The channel of a window that currently has touch focus.
* @param toChannel The channel of the window that should receive touch focus in
* place of the first.
+ * @param isDragDrop True if transfer touch focus for drag and drop.
* @return True if the transfer was successful. False if the window with the
* specified channel did not actually have touch focus at the time of the request.
*/
public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
- @NonNull InputChannel toChannel) {
- return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken());
+ @NonNull InputChannel toChannel, boolean isDragDrop) {
+ return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken(),
+ isDragDrop);
}
/**
@@ -1757,7 +1754,8 @@ public class InputManagerService extends IInputManager.Stub
@NonNull IBinder toChannelToken) {
Objects.nonNull(fromChannelToken);
Objects.nonNull(toChannelToken);
- return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken);
+ return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken,
+ false /* isDragDrop */);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8285e32ed8db..ffb532ebcca4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2381,9 +2381,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
+ final InputMethodInfo curInputMethodInfo = mMethodMap.get(mCurId);
+ final boolean suppressesSpellChecker =
+ curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
- mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
+ mCurId, mCurSeq, mCurActivityViewToScreenMatrix, suppressesSpellChecker);
}
@Nullable
@@ -2425,7 +2428,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, mCurMethodId, mCurSeq, null);
+ null, null, mCurMethodId, mCurSeq, null, false);
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2501,7 +2504,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
requestClientSessionLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, mCurId, mCurSeq, null);
+ null, null, mCurId, mCurSeq, null, false);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
@@ -2513,7 +2516,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq, null);
+ null, null, mCurId, mCurSeq, null, false);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -2553,7 +2556,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq, null);
+ null, null, mCurId, mCurSeq, null, false);
}
mCurIntent = null;
Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -2920,8 +2923,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0);
break;
}
- mWindowManagerInternal.updateInputMethodWindowStatus(token,
- (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
+ mWindowManagerInternal.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
}
@BinderThread
@@ -3509,7 +3511,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, null);
+ null, null, null, -1, null, false);
}
mCurFocusedWindow = windowToken;
@@ -5836,7 +5838,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@ShellCommandResult
private int handleShellCommandTraceInputMethod(@NonNull ShellCommand shellCommand) {
- int result = ImeTracing.getInstance().onShellCommand(shellCommand);
+ final String cmd = shellCommand.getNextArgRequired();
+ final PrintWriter pw = shellCommand.getOutPrintWriter();
+ switch (cmd) {
+ case "start":
+ ImeTracing.getInstance().getInstance().startTrace(pw);
+ break;
+ case "stop":
+ ImeTracing.getInstance().stopTrace(pw);
+ break;
+ default:
+ pw.println("Unknown command: " + cmd);
+ pw.println("Input method trace options:");
+ pw.println(" start: Start tracing");
+ pw.println(" stop: Stop tracing");
+ return ShellCommandResult.FAILURE;
+ }
boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
ArrayMap<IBinder, ClientState> clients;
synchronized (mMethodMap) {
@@ -5852,7 +5869,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
}
- return result;
+ return ShellCommandResult.SUCCESS;
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index b13c307497d9..7e5e427cf0a1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Printer;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
@@ -502,7 +501,7 @@ final class InputMethodSubtypeSwitchingController {
public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
if (mController == null) {
if (DEBUG) {
- Log.e(TAG, "mController shouldn't be null.");
+ Slog.e(TAG, "mController shouldn't be null.");
}
return;
}
@@ -520,7 +519,7 @@ final class InputMethodSubtypeSwitchingController {
InputMethodSubtype subtype) {
if (mController == null) {
if (DEBUG) {
- Log.e(TAG, "mController shouldn't be null.");
+ Slog.e(TAG, "mController shouldn't be null.");
}
return null;
}
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1dd3d4190e8a..ef1489b4adf9 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1748,7 +1748,7 @@ public final class MultiClientInputMethodManagerService {
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
null, null, data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, null);
+ clientInfo.mBindingSequence, null, false);
case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
clientInfo.mBindingSequence++;
@@ -1770,7 +1770,7 @@ public final class MultiClientInputMethodManagerService {
clientInfo.mInputMethodSession,
clientInfo.mWriteChannel.dup(),
data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, null);
+ clientInfo.mBindingSequence, null, false);
case InputMethodClientState.UNREGISTERED:
Slog.e(TAG, "The client is already unregistered.");
return InputBindResult.INVALID_CLIENT;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index e1c011d821a7..c8c212b3109c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -47,9 +47,11 @@ import android.util.proto.ProtoOutputStream;
import com.android.server.location.ClientBrokerProto;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -205,14 +207,19 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* allowed to communicate over that channel. A channel is defined to have been opened if the
* client has sent or received messages from the particular nanoapp.
*/
- private final Map<Long, Integer> mMessageChannelNanoappIdMap =
- new ConcurrentHashMap<Long, Integer>();
+ private final Map<Long, Integer> mMessageChannelNanoappIdMap = new ConcurrentHashMap<>();
+
+ /**
+ * Set containing all nanoapps that have been forcefully transitioned to the denied
+ * authorization state (via CLI) to ensure they don't transition back to the granted state
+ * later if, for example, a permission check is performed due to another nanoapp
+ */
+ private final Set<Long> mForceDeniedNapps = new HashSet<>();
/**
* Map containing all nanoapps that have active auth state denial timers.
*/
- private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap =
- new ConcurrentHashMap<Long, AuthStateDenialTimer>();
+ private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap = new ConcurrentHashMap<>();
/**
* Callback used to obtain the latest set of nanoapp permissions and verify this client has
@@ -637,7 +644,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private int updateNanoAppAuthState(
long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired) {
return updateNanoAppAuthState(
- nanoAppId, nanoappPermissions, gracePeriodExpired, false /* forceDenied */);
+ nanoAppId, nanoappPermissions, gracePeriodExpired,
+ mForceDeniedNapps.contains(nanoAppId) /* forceDenied */);
}
/**
@@ -679,6 +687,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
// any state -> DENIED if "forceDenied" is true
if (forceDenied) {
newAuthState = AUTHORIZATION_DENIED;
+ mForceDeniedNapps.add(nanoAppId);
} else if (gracePeriodExpired) {
if (curAuthState == AUTHORIZATION_DENIED_GRACE_PERIOD) {
newAuthState = AUTHORIZATION_DENIED;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index dde45c4ab52a..c44089b7817f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -17,13 +17,16 @@
package com.android.server.location.contexthub;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -59,6 +62,7 @@ import android.util.Pair;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
import com.android.server.location.ContextHubServiceProto;
import java.io.FileDescriptor;
@@ -127,6 +131,8 @@ public class ContextHubService extends IContextHubService.Stub {
// Lock object for sendWifiSettingUpdate()
private final Object mSendWifiSettingUpdateLock = new Object();
+ private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
+
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -186,6 +192,7 @@ public class ContextHubService extends IContextHubService.Stub {
if (mContextHubWrapper == null) {
mTransactionManager = null;
mClientManager = null;
+ mSensorPrivacyManagerInternal = null;
mDefaultClientMap = Collections.emptyMap();
mContextHubIdToInfoMap = Collections.emptyMap();
mSupportedContextHubPerms = Collections.emptyList();
@@ -208,6 +215,8 @@ public class ContextHubService extends IContextHubService.Stub {
mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
mTransactionManager = new ContextHubTransactionManager(
mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager);
+ mSensorPrivacyManagerInternal =
+ LocalServices.getService(SensorPrivacyManagerInternal.class);
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
@@ -284,18 +293,16 @@ public class ContextHubService extends IContextHubService.Stub {
}
if (mContextHubWrapper.supportsMicrophoneDisableSettingNotifications()) {
- sendMicrophoneDisableSettingUpdate();
+ sendMicrophoneDisableSettingUpdateForCurrentUser();
+
+ mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
+ SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
+ if (userId == getCurrentUserId()) {
+ Log.d(TAG, "User: " + userId + " enabled: " + enabled);
+ sendMicrophoneDisableSettingUpdate(enabled);
+ }
+ });
- SensorPrivacyManager.OnSensorPrivacyChangedListener listener =
- new SensorPrivacyManager.OnSensorPrivacyChangedListener() {
- @Override
- public void onSensorPrivacyChanged(boolean enabled) {
- sendMicrophoneDisableSettingUpdate();
- }
- };
- SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
- manager.addSensorPrivacyListener(
- SensorPrivacyManager.Sensors.MICROPHONE, listener);
}
}
@@ -1074,19 +1081,48 @@ public class ContextHubService extends IContextHubService.Stub {
}
/**
- * Obtains the latest microphone disable setting value and notifies the
- * Context Hub.
+ * Notifies a microphone disable settings change to the Context Hub.
*/
- private void sendMicrophoneDisableSettingUpdate() {
- SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
- boolean disabled = manager.isSensorPrivacyEnabled(
- SensorPrivacyManager.Sensors.MICROPHONE);
- Log.d(TAG, "Mic Disabled Setting: " + disabled);
- mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
+ private void sendMicrophoneDisableSettingUpdate(boolean enabled) {
+ Log.d(TAG, "Mic Disabled Setting: " + enabled);
+ mContextHubWrapper.onMicrophoneDisableSettingChanged(enabled);
+ }
+
+ /**
+ * Obtains the latest microphone disabled setting for the current user
+ * and notifies the Context Hub.
+ */
+ private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
+ boolean isEnabled = mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
+ getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
+ sendMicrophoneDisableSettingUpdate(isEnabled);
}
private String getCallingPackageName() {
return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
+
+ private int getCurrentUserId() {
+ final long id = Binder.clearCallingIdentity();
+ try {
+ UserInfo currentUser = ActivityManager.getService().getCurrentUser();
+ return currentUser.id;
+ } catch (RemoteException e) {
+ // Activity manager not running, nothing we can do - assume user 0.
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ return UserHandle.USER_SYSTEM;
+ }
+
+ /**
+ * Send a microphone disable settings update whenever the foreground user changes.
+ * We always send a settings update regardless of the previous state for the same user
+ * since the CHRE framework is expected to handle repeated identical setting update.
+ */
+ public void onUserChanged() {
+ Log.d(TAG, "User changed to id: " + getCurrentUserId());
+ sendMicrophoneDisableSettingUpdateForCurrentUser();
+ }
}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 5a90fa7a271c..7f47805ee121 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -370,6 +370,7 @@ public class GeofenceManager extends
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_GEOFENCE,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
null,
/* LocationRequest= */ null,
/* hasListener= */ false,
@@ -383,6 +384,7 @@ public class GeofenceManager extends
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_GEOFENCE,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
null,
/* LocationRequest= */ null,
/* hasListener= */ false,
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index b3119d7aa53e..8460d6797543 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -189,6 +189,7 @@ public final class GnssMeasurementsProvider extends
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
null,
null,
true,
@@ -202,6 +203,7 @@ public final class GnssMeasurementsProvider extends
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
null,
null,
true,
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 1eb16184685d..936283deda8e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -83,6 +83,7 @@ public class GnssStatusProvider extends
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
null,
null,
true,
@@ -96,6 +97,7 @@ public class GnssStatusProvider extends
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
null,
null,
true,
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index f275663a1309..1eef0de3a05d 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -27,6 +27,7 @@ import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.Location;
+import android.os.Binder;
import android.os.SystemClock;
import android.util.Log;
@@ -921,6 +922,7 @@ public class GnssNative {
@NativeEntryPoint
void reportGnssServiceDied() {
+ // Not necessary to clear (and restore) binder identity since it runs on another thread.
Log.e(TAG, "gnss hal died - restarting shortly...");
// move to another thread just in case there is some awkward gnss thread dependency with
@@ -940,96 +942,111 @@ public class GnssNative {
@NativeEntryPoint
void reportLocation(boolean hasLatLong, Location location) {
- if (hasLatLong && !mHasFirstFix) {
- mHasFirstFix = true;
-
- // notify status listeners
- int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
- for (int i = 0; i < mStatusCallbacks.length; i++) {
- mStatusCallbacks[i].onReportFirstFix(ttff);
+ Binder.withCleanCallingIdentity(() -> {
+ if (hasLatLong && !mHasFirstFix) {
+ mHasFirstFix = true;
+
+ // notify status listeners
+ int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
+ for (int i = 0; i < mStatusCallbacks.length; i++) {
+ mStatusCallbacks[i].onReportFirstFix(ttff);
+ }
}
- }
- if (location.hasSpeed()) {
- boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
- if (!mItarSpeedLimitExceeded && exceeded) {
- Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
- } else if (mItarSpeedLimitExceeded && !exceeded) {
- Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+ if (location.hasSpeed()) {
+ boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
+ if (!mItarSpeedLimitExceeded && exceeded) {
+ Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
+ } else if (mItarSpeedLimitExceeded && !exceeded) {
+ Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+ }
+ mItarSpeedLimitExceeded = exceeded;
}
- mItarSpeedLimitExceeded = exceeded;
- }
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mLocationCallbacks.length; i++) {
- mLocationCallbacks[i].onReportLocation(hasLatLong, location);
- }
+ for (int i = 0; i < mLocationCallbacks.length; i++) {
+ mLocationCallbacks[i].onReportLocation(hasLatLong, location);
+ }
+ });
}
@NativeEntryPoint
void reportStatus(@StatusCallbacks.GnssStatusValue int gnssStatus) {
- for (int i = 0; i < mStatusCallbacks.length; i++) {
- mStatusCallbacks[i].onReportStatus(gnssStatus);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ for (int i = 0; i < mStatusCallbacks.length; i++) {
+ mStatusCallbacks[i].onReportStatus(gnssStatus);
+ }
+ });
}
@NativeEntryPoint
void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
float[] elevations, float[] azimuths, float[] carrierFrequencies,
float[] basebandCn0DbHzs) {
- GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
- azimuths, carrierFrequencies, basebandCn0DbHzs);
- for (int i = 0; i < mSvStatusCallbacks.length; i++) {
- mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
+ azimuths, carrierFrequencies, basebandCn0DbHzs);
+ for (int i = 0; i < mSvStatusCallbacks.length; i++) {
+ mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
+ }
+ });
}
@NativeEntryPoint
void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
- mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
+ Binder.withCleanCallingIdentity(
+ () -> mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr));
}
@NativeEntryPoint
void reportNmea(long timestamp) {
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mNmeaCallbacks.length; i++) {
- mNmeaCallbacks[i].onReportNmea(timestamp);
- }
+ for (int i = 0; i < mNmeaCallbacks.length; i++) {
+ mNmeaCallbacks[i].onReportNmea(timestamp);
+ }
+ });
}
@NativeEntryPoint
void reportMeasurementData(GnssMeasurementsEvent event) {
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mMeasurementCallbacks.length; i++) {
- mMeasurementCallbacks[i].onReportMeasurements(event);
- }
+ for (int i = 0; i < mMeasurementCallbacks.length; i++) {
+ mMeasurementCallbacks[i].onReportMeasurements(event);
+ }
+ });
}
@NativeEntryPoint
void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
- for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
- mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
+ mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
+ }
+ });
}
@NativeEntryPoint
void reportNavigationMessage(GnssNavigationMessage event) {
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
- mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
- }
+ for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
+ mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
+ }
+ });
}
@NativeEntryPoint
@@ -1061,15 +1078,17 @@ public class GnssNative {
private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
- if (newCapabilities.equals(oldCapabilities)) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (newCapabilities.equals(oldCapabilities)) {
+ return;
+ }
- Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
+ Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
- for (int i = 0; i < mBaseCallbacks.length; i++) {
- mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
- }
+ for (int i = 0; i < mBaseCallbacks.length; i++) {
+ mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
+ }
+ });
}
@NativeEntryPoint
@@ -1089,88 +1108,103 @@ public class GnssNative {
@NativeEntryPoint
void reportLocationBatch(Location[] locations) {
- for (int i = 0; i < mLocationCallbacks.length; i++) {
- mLocationCallbacks[i].onReportLocations(locations);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ for (int i = 0; i < mLocationCallbacks.length; i++) {
+ mLocationCallbacks[i].onReportLocations(locations);
+ }
+ });
}
@NativeEntryPoint
void psdsDownloadRequest(int psdsType) {
- mPsdsCallbacks.onRequestPsdsDownload(psdsType);
+ Binder.withCleanCallingIdentity(() -> mPsdsCallbacks.onRequestPsdsDownload(psdsType));
}
@NativeEntryPoint
void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
- mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, transition,
- transitionTimestamp);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location,
+ transition, transitionTimestamp));
}
@NativeEntryPoint
void reportGeofenceStatus(int status, Location location) {
- mGeofenceCallbacks.onReportGeofenceStatus(status, location);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceStatus(status, location));
}
@NativeEntryPoint
void reportGeofenceAddStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status));
}
@NativeEntryPoint
void reportGeofenceRemoveStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status));
}
@NativeEntryPoint
void reportGeofencePauseStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status));
}
@NativeEntryPoint
void reportGeofenceResumeStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status));
}
@NativeEntryPoint
void reportNiNotification(int notificationId, int niType, int notifyFlags,
int timeout, int defaultResponse, String requestorId, String text,
int requestorIdEncoding, int textEncoding) {
- mNotificationCallbacks.onReportNiNotification(notificationId, niType, notifyFlags, timeout,
- defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
+ Binder.withCleanCallingIdentity(
+ () -> mNotificationCallbacks.onReportNiNotification(notificationId, niType,
+ notifyFlags, timeout, defaultResponse, requestorId, text,
+ requestorIdEncoding, textEncoding));
}
@NativeEntryPoint
void requestSetID(int flags) {
- mAGpsCallbacks.onRequestSetID(flags);
+ Binder.withCleanCallingIdentity(() -> mAGpsCallbacks.onRequestSetID(flags));
}
@NativeEntryPoint
void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
- mLocationRequestCallbacks.onRequestLocation(independentFromGnss, isUserEmergency);
+ Binder.withCleanCallingIdentity(
+ () -> mLocationRequestCallbacks.onRequestLocation(independentFromGnss,
+ isUserEmergency));
}
@NativeEntryPoint
void requestUtcTime() {
- mTimeCallbacks.onRequestUtcTime();
+ Binder.withCleanCallingIdentity(() -> mTimeCallbacks.onRequestUtcTime());
}
@NativeEntryPoint
void requestRefLocation() {
- mLocationRequestCallbacks.onRequestRefLocation();
+ Binder.withCleanCallingIdentity(
+ () -> mLocationRequestCallbacks.onRequestRefLocation());
}
@NativeEntryPoint
void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId,
byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
- mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, protocolStack,
- otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
- isCachedLocation);
+ Binder.withCleanCallingIdentity(
+ () -> mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName,
+ protocolStack, otherProtocolStackName, requestor, requestorId, responseType,
+ inEmergencyMode, isCachedLocation));
}
@NativeEntryPoint
boolean isInEmergencySession() {
- return mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec());
+ return Binder.withCleanCallingIdentity(
+ () -> mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec()));
}
/**
diff --git a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
index 244a8e0daca0..af21bcbfb0ef 100644
--- a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
@@ -49,9 +49,9 @@ public class LocationUsageLogger {
* Log a location API usage event.
*/
public void logLocationApiUsage(int usageType, int apiInUse,
- String packageName, String provider, LocationRequest locationRequest,
- boolean hasListener, boolean hasIntent,
- Geofence geofence, boolean foreground) {
+ String packageName, String attributionTag, String provider,
+ LocationRequest locationRequest, boolean hasListener,
+ boolean hasIntent, Geofence geofence, boolean foreground) {
try {
if (hitApiUsageLogCap()) {
return;
@@ -84,7 +84,8 @@ public class LocationUsageLogger {
isGeofenceNull
? LocationStatsEnums.RADIUS_UNKNOWN
: bucketizeRadius(geofence.getRadius()),
- categorizeActivityImportance(foreground));
+ categorizeActivityImportance(foreground),
+ attributionTag);
} catch (Exception e) {
// Swallow exceptions to avoid crashing LMS.
Log.w(TAG, "Failed to log API usage to statsd.", e);
@@ -114,7 +115,8 @@ public class LocationUsageLogger {
/* isListenerNull= */ true,
/* isIntentNull= */ true),
/* bucketizedRadius= */ 0,
- LocationStatsEnums.IMPORTANCE_UNKNOWN);
+ LocationStatsEnums.IMPORTANCE_UNKNOWN,
+ /* attribution_tag */ null);
} catch (Exception e) {
Log.w(TAG, "Failed to log API usage to statsd.", e);
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 2aa6f2869afb..dc8b1d001c74 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1854,6 +1854,7 @@ public class LocationProviderManager extends
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
mName,
registration.getRequest(),
key instanceof PendingIntent,
@@ -1882,6 +1883,7 @@ public class LocationProviderManager extends
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
registration.getIdentity().getPackageName(),
+ registration.getIdentity().getAttributionTag(),
mName,
registration.getRequest(),
key instanceof PendingIntent,
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 82b0f9c05b6b..6201b9414aaf 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -119,38 +119,25 @@ public class BiometricDeferredQueue {
for (UserAuthInfo userAuthInfo : pendingResetLockuts) {
Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId
+ ", user: " + userAuthInfo.userId);
- final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
- getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
- userAuthInfo.userId);
- if (response == null) {
- Slog.wtf(TAG, "VerifyChallenge failed, null response");
- continue;
+ final byte[] hat = requestHatFromGatekeeperPassword(spManager, userAuthInfo,
+ challenge);
+ if (hat != null) {
+ faceManager.resetLockout(sensorId, userAuthInfo.userId, hat);
}
- if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
- Slog.wtf(TAG, "VerifyChallenge failed, response: "
- + response.getResponseCode());
- }
- faceManager.resetLockout(sensorId, userAuthInfo.userId,
- response.getGatekeeperHAT());
}
sensorIds.remove(sensorId);
- faceManager.revokeChallenge(sensorId);
+ // Challenge is only required for IBiometricsFace@1.0 (and not IFace AIDL). The
+ // IBiometricsFace@1.0 HAL does not require userId to revokeChallenge, so passing
+ // in 0 is OK.
+ final int userId = 0;
+ faceManager.revokeChallenge(sensorId, userId, challenge);
if (sensorIds.isEmpty()) {
Slog.d(TAG, "Done requesting resetLockout for all face sensors");
finishCallback.onFinished();
}
}
-
- synchronized IGateKeeperService getGatekeeperService() {
- final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
- if (service == null) {
- Slog.e(TAG, "Unable to acquire GateKeeperService");
- return null;
- }
- return IGateKeeperService.Stub.asInterface(service);
- }
}
@Nullable private FaceResetLockoutTask mFaceResetLockoutTask;
@@ -210,10 +197,19 @@ public class BiometricDeferredQueue {
mFingerprintManager.resetLockout(prop.sensorId, user.userId,
null /* hardwareAuthToken */);
}
+ } else if (!prop.resetLockoutRequiresChallenge) {
+ for (UserAuthInfo user : pendingResetLockouts) {
+ Slog.d(TAG, "Resetting fingerprint lockout for sensor: " + prop.sensorId
+ + ", user: " + user.userId);
+ final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
+ 0 /* challenge */);
+ if (hat != null) {
+ mFingerprintManager.resetLockout(prop.sensorId, user.userId, hat);
+ }
+ }
} else {
- Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet");
- // TODO(b/152414803): Implement this when resetLockout is implemented below
- // the framework.
+ Slog.w(TAG, "No fingerprint HAL interface requires HAT with challenge"
+ + ", sensorId: " + prop.sensorId);
}
}
}
@@ -224,11 +220,6 @@ public class BiometricDeferredQueue {
* in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
* hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
* challenge, or other race conditions.
- *
- * TODO(b/162965646) This logic can be avoided if multiple in-flight challenges are supported.
- * Though it will need to continue to exist to support existing HIDLs, each profile that
- * requires resetLockout could have its own challenge, and the `mPendingResetLockouts` queue
- * can be avoided.
*/
private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
if (mFaceManager != null) {
@@ -247,10 +238,60 @@ public class BiometricDeferredQueue {
mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager,
mSpManager, sensorIds, pendingResetLockouts);
for (final FaceSensorPropertiesInternal prop : faceSensorProperties) {
- // Generate a challenge for each sensor. The challenge does not need to be
- // per-user, since the HAT returned by gatekeeper contains userId.
- mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
+ if (prop.resetLockoutRequiresHardwareAuthToken) {
+ if (prop.resetLockoutRequiresChallenge) {
+ // Generate a challenge for each sensor. The challenge does not need to be
+ // per-user, since the HAT returned by gatekeeper contains userId.
+ mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
+ } else {
+ for (UserAuthInfo user : pendingResetLockouts) {
+ Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
+ + ", user: " + user.userId);
+ final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
+ 0 /* challenge */);
+ if (hat != null) {
+ mFaceManager.resetLockout(prop.sensorId, user.userId, hat);
+ }
+ }
+ }
+ } else {
+ Slog.w(TAG, "Lockout is below the HAL for all face authentication interfaces"
+ + ", sensorId: " + prop.sensorId);
+ }
}
}
}
+
+ @Nullable
+ private static byte[] requestHatFromGatekeeperPassword(
+ @NonNull SyntheticPasswordManager spManager,
+ @NonNull UserAuthInfo userAuthInfo, long challenge) {
+ final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
+ getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
+ userAuthInfo.userId);
+ if (response == null) {
+ Slog.wtf(TAG, "VerifyChallenge failed, null response");
+ return null;
+ }
+ if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ Slog.wtf(TAG, "VerifyChallenge failed, response: "
+ + response.getResponseCode());
+ return null;
+ }
+ if (response.getGatekeeperHAT() == null) {
+ Slog.e(TAG, "Null HAT received from spManager");
+ }
+
+ return response.getGatekeeperHAT();
+ }
+
+ @Nullable
+ private static synchronized IGateKeeperService getGatekeeperService() {
+ final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
+ if (service == null) {
+ Slog.e(TAG, "Unable to acquire GateKeeperService");
+ return null;
+ }
+ return IGateKeeperService.Stub.asInterface(service);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 43c736544ee6..22a9a47c9651 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -91,7 +91,6 @@ import android.provider.Settings.SettingNotFoundException;
import android.security.AndroidKeyStoreMaintenance;
import android.security.Authorization;
import android.security.KeyStore;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.UserNotAuthenticatedException;
@@ -158,7 +157,6 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
-import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -263,13 +261,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void onStart() {
- Optional<Boolean> keystore2_enabled =
- android.sysprop.Keystore2Properties.keystore2_enabled();
- if (keystore2_enabled.isPresent() && keystore2_enabled.get()) {
- android.security.keystore2.AndroidKeyStoreProvider.install();
- } else {
- AndroidKeyStoreProvider.install();
- }
+ android.security.keystore2.AndroidKeyStoreProvider.install();
mLockSettingsService = new LockSettingsService(getContext());
publishBinderService("lock_settings", mLockSettingsService);
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 48382a946d4b..6e99cba6ea91 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -64,6 +64,8 @@ class RebootEscrowManager {
@VisibleForTesting
public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count";
+ static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp";
+
/**
* Number of boots until we consider the escrow data to be stale for the purposes of metrics.
* <p>
@@ -144,8 +146,7 @@ class RebootEscrowManager {
private RebootEscrowProviderInterface createRebootEscrowProvider() {
RebootEscrowProviderInterface rebootEscrowProvider;
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
- "server_based_ror_enabled", false)) {
+ if (serverBasedResumeOnReboot()) {
Slog.i(TAG, "Using server based resume on reboot");
rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
@@ -166,6 +167,11 @@ class RebootEscrowManager {
handler.postDelayed(runnable, delayMillis);
}
+ public boolean serverBasedResumeOnReboot() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
+ "server_based_ror_enabled", false);
+ }
+
public Context getContext() {
return mContext;
}
@@ -204,10 +210,12 @@ class RebootEscrowManager {
DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
}
- public void reportMetric(boolean success) {
- // TODO(b/179105110) design error code; and report the true value for other fields.
- FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1,
- -1, 0, -1);
+ public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+ int escrowDurationInSeconds, int vbmetaDigestStatus,
+ int durationSinceBootCompleteInSeconds) {
+ FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success,
+ errorCode, serviceType, attemptCount, escrowDurationInSeconds,
+ vbmetaDigestStatus, durationSinceBootCompleteInSeconds);
}
public RebootEscrowEventLog getEventLog() {
@@ -230,7 +238,7 @@ class RebootEscrowManager {
mKeyStoreManager = injector.getKeyStoreManager();
}
- private void onGetRebootEscrowKeyFailed(List<UserInfo> users) {
+ private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) {
Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
for (UserInfo user : users) {
mStorage.removeRebootEscrow(user.id);
@@ -238,7 +246,7 @@ class RebootEscrowManager {
// Clear the old key in keystore.
mKeyStoreManager.clearKeyStoreEncryptionKey();
- onEscrowRestoreComplete(false);
+ onEscrowRestoreComplete(false, attemptCount);
}
void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
@@ -251,6 +259,8 @@ class RebootEscrowManager {
}
if (rebootEscrowUsers.isEmpty()) {
+ Slog.i(TAG, "No reboot escrow data found for users,"
+ + " skipping loading escrow data");
return;
}
@@ -274,7 +284,7 @@ class RebootEscrowManager {
}
Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
- onGetRebootEscrowKeyFailed(users);
+ onGetRebootEscrowKeyFailed(users, attemptNumber);
}
void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
@@ -297,7 +307,7 @@ class RebootEscrowManager {
}
if (escrowKey == null) {
- onGetRebootEscrowKeyFailed(users);
+ onGetRebootEscrowKeyFailed(users, attemptNumber + 1);
return;
}
@@ -311,16 +321,35 @@ class RebootEscrowManager {
// Clear the old key in keystore. A new key will be generated by new RoR requests.
mKeyStoreManager.clearKeyStoreEncryptionKey();
- onEscrowRestoreComplete(allUsersUnlocked);
+ onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1);
+ }
+
+ private void reportMetricOnRestoreComplete(boolean success, int attemptCount) {
+ int serviceType = mInjector.serverBasedResumeOnReboot()
+ ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED
+ : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL;
+
+ long armedTimestamp = mStorage.getLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, -1,
+ USER_SYSTEM);
+ mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM);
+ int escrowDurationInSeconds = armedTimestamp != -1
+ ? (int) (System.currentTimeMillis() - armedTimestamp) / 1000 : -1;
+
+ // TODO(b/179105110) design error code; and report the true value for other fields.
+ int vbmetaDigestStatus = FrameworkStatsLog
+ .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT;
+
+ mInjector.reportMetric(success, 0 /* error code */, serviceType, attemptCount,
+ escrowDurationInSeconds, vbmetaDigestStatus, -1);
}
- private void onEscrowRestoreComplete(boolean success) {
+ private void onEscrowRestoreComplete(boolean success, int attemptCount) {
int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
int bootCountDelta = mInjector.getBootCount() - previousBootCount;
if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
- mInjector.reportMetric(success);
+ reportMetricOnRestoreComplete(success, attemptCount);
}
}
@@ -476,6 +505,8 @@ class RebootEscrowManager {
boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
if (armedRebootEscrow) {
mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
+ mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, System.currentTimeMillis(),
+ USER_SYSTEM);
mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
index 35571f1f2728..e75aae1f99aa 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
@@ -16,7 +16,7 @@
package com.android.server.locksettings.recoverablekeystore;
-import android.security.keystore.AndroidKeyStoreSecretKey;
+import javax.crypto.SecretKey;
/**
* Used to unwrap recoverable keys before syncing them with remote storage.
@@ -30,7 +30,7 @@ import android.security.keystore.AndroidKeyStoreSecretKey;
public class PlatformDecryptionKey {
private final int mGenerationId;
- private final AndroidKeyStoreSecretKey mKey;
+ private final SecretKey mKey;
/**
* A new instance.
@@ -40,7 +40,7 @@ public class PlatformDecryptionKey {
*
* @hide
*/
- public PlatformDecryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+ public PlatformDecryptionKey(int generationId, SecretKey key) {
mGenerationId = generationId;
mKey = key;
}
@@ -59,7 +59,7 @@ public class PlatformDecryptionKey {
*
* @hide
*/
- public AndroidKeyStoreSecretKey getKey() {
+ public SecretKey getKey() {
return mKey;
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
index 38f5b45ea190..ee334462f7be 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
@@ -16,7 +16,7 @@
package com.android.server.locksettings.recoverablekeystore;
-import android.security.keystore.AndroidKeyStoreSecretKey;
+import javax.crypto.SecretKey;
/**
* Private key stored in AndroidKeyStore. Used to wrap recoverable keys before writing them to disk.
@@ -33,7 +33,7 @@ import android.security.keystore.AndroidKeyStoreSecretKey;
public class PlatformEncryptionKey {
private final int mGenerationId;
- private final AndroidKeyStoreSecretKey mKey;
+ private final SecretKey mKey;
/**
* A new instance.
@@ -41,7 +41,7 @@ public class PlatformEncryptionKey {
* @param generationId The generation ID of the key.
* @param key The secret key handle. Can be used to encrypt WITHOUT requiring screen unlock.
*/
- public PlatformEncryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+ public PlatformEncryptionKey(int generationId, SecretKey key) {
mGenerationId = generationId;
mKey = key;
}
@@ -56,7 +56,7 @@ public class PlatformEncryptionKey {
/**
* Returns the actual key, which can only be used to encrypt.
*/
- public AndroidKeyStoreSecretKey getKey() {
+ public SecretKey getKey() {
return mKey;
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index f448a6ef8c0b..f32af5434c43 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.GateKeeper;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
@@ -237,7 +236,7 @@ public class PlatformKeyManager {
if (!isKeyLoaded(userId, generationId)) {
throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
}
- AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+ SecretKey key = (SecretKey) mKeyStore.getKey(
alias, /*password=*/ null);
return new PlatformEncryptionKey(generationId, key);
}
@@ -289,7 +288,7 @@ public class PlatformKeyManager {
if (!isKeyLoaded(userId, generationId)) {
throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
}
- AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+ SecretKey key = (SecretKey) mKeyStore.getKey(
alias, /*password=*/ null);
return new PlatformDecryptionKey(generationId, key);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 0a074e1e7c50..b10d56b62acc 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1068,6 +1068,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
public boolean sendMediaButton(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
try {
+ if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
+ final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
+ + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
+ }
if (asSystemService) {
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
@@ -1085,6 +1091,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
public boolean sendMediaButton(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent) {
try {
+ if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
+ final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
+ + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
+ }
if (asSystemService) {
mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 23d84298b41e..46ece74180fe 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -63,6 +63,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
+import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -82,9 +83,11 @@ import android.view.ViewConfiguration;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
+import com.android.server.am.ActivityManagerLocal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -114,6 +117,13 @@ public class MediaSessionService extends SystemService implements Monitor {
*/
private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+ /**
+ * Denotes the duration during which an app receiving a media session callback will be
+ * exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if it is
+ * in the background state while it receives a media session callback.
+ */
+ private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
+
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
@@ -136,6 +146,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private KeyguardManager mKeyguardManager;
private AudioManager mAudioManager;
private boolean mHasFeatureLeanback;
+ private ActivityManagerLocal mActivityManagerLocal;
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -219,6 +230,8 @@ public class MediaSessionService extends SystemService implements Monitor {
final IntentFilter filter = new IntentFilter(
NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED);
mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter);
+
+ mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class);
}
@Override
@@ -538,6 +551,26 @@ public class MediaSessionService extends SystemService implements Monitor {
throw new IllegalArgumentException("packageName is not owned by the calling process");
}
+ void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
+ int callingPid, int callingUid, String callingPackage, String reason) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ enforcePackageName(callingPackage, callingUid);
+ if (targetUid != callingUid && mActivityManagerLocal.canStartForegroundService(
+ callingPid, callingUid, callingPackage)) {
+ final Context userContext = mContext.createContextAsUser(
+ UserHandle.of(UserHandle.getUserId(targetUid)), /* flags= */ 0);
+ final PowerExemptionManager powerExemptionManager = userContext.getSystemService(
+ PowerExemptionManager.class);
+ powerExemptionManager.addToTemporaryAllowList(targetPackage,
+ FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS,
+ PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
/**
* Checks a caller's authorization to register an IRemoteControlDisplay.
* Authorization is granted if one of the following is true:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index da62aca70cd3..735381873c05 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -58,6 +58,23 @@ import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_MASK;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMPTED;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_ADMIN_DISABLED;
+import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_MASK;
+import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_DOZE;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_RESTRICTED_MODE;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
@@ -413,6 +430,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20;
+ // TODO: Add similar docs for other messages.
+ /**
+ * Message to indicate that reasons for why an uid is blocked changed.
+ * arg1 = uid
+ * arg2 = oldBlockedReasons
+ * obj = newBlockedReasons
+ */
+ private static final int MSG_BLOCKED_REASON_CHANGED = 21;
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -559,7 +584,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Foreground at UID granularity. */
@GuardedBy("mUidRulesFirstLock")
- final SparseArray<UidState> mUidState = new SparseArray<UidState>();
+ private final SparseArray<UidState> mUidState = new SparseArray<>();
+
+ @GuardedBy("mUidRulesFirstLock")
+ private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>();
/** Map from network ID to last observed meteredness state */
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -2876,15 +2904,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
- public void registerListener(INetworkPolicyListener listener) {
+ public void registerListener(@NonNull INetworkPolicyListener listener) {
+ Objects.requireNonNull(listener);
// TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps
// have declared OBSERVE_NETWORK_POLICY.
enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY);
mListeners.register(listener);
+ // TODO: Send callbacks to the newly registered listener
}
@Override
- public void unregisterListener(INetworkPolicyListener listener) {
+ public void unregisterListener(@NonNull INetworkPolicyListener listener) {
+ Objects.requireNonNull(listener);
// TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps
// have declared OBSERVE_NETWORK_POLICY.
enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY);
@@ -3921,6 +3952,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mUidRules.put(uid, newUidRule);
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
}
+ updateBlockedReasonsForRestrictedModeUL(uid);
});
if (mRestrictedNetworkingMode) {
// firewall rules only need to be set when this mode is being enabled.
@@ -3941,6 +3973,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mUidRules.put(uid, newUidRule);
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
}
+ updateBlockedReasonsForRestrictedModeUL(uid);
// if restricted networking mode is on, and the app has an access exemption, the uid rule
// will not change, but the firewall rule will have to be updated.
@@ -3952,6 +3985,31 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ private void updateBlockedReasonsForRestrictedModeUL(int uid) {
+ UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ if (uidBlockedState == null) {
+ uidBlockedState = new UidBlockedState();
+ mUidBlockedState.put(uid, uidBlockedState);
+ }
+ final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ if (mRestrictedNetworkingMode) {
+ uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
+ } else {
+ uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
+ }
+ if (hasRestrictedModeAccess(uid)) {
+ uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+ } else {
+ uidBlockedState.allowedReasons &= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+ }
+ uidBlockedState.updateEffectiveBlockedReasons();
+ if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+ mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+ uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+ .sendToTarget();
+ }
+ }
+
private int getNewRestrictedModeUidRule(int uid, int oldUidRule) {
int newRule = oldUidRule;
newRule &= ~MASK_RESTRICTED_MODE_NETWORKS;
@@ -4072,11 +4130,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
|| mPowerSaveWhitelistAppIds.get(appId);
if (!deviceIdleMode) {
- isWhitelisted = isWhitelisted || mPowerSaveWhitelistExceptIdleAppIds.get(appId);
+ isWhitelisted = isWhitelisted || isWhitelistedFromPowerSaveExceptIdleUL(uid);
}
return isWhitelisted;
}
+ /**
+ * Returns whether a uid is allowlisted from power saving restrictions, except Device idle
+ * (eg: Battery Saver and app idle).
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private boolean isWhitelistedFromPowerSaveExceptIdleUL(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ return mPowerSaveWhitelistExceptIdleAppIds.get(appId);
+ }
+
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
@@ -4521,6 +4589,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
+ UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ if (uidBlockedState == null) {
+ uidBlockedState = new UidBlockedState();
+ mUidBlockedState.put(uid, uidBlockedState);
+ }
final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
@@ -4545,6 +4618,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ int newBlockedReasons = BLOCKED_REASON_NONE;
+ int newAllowedReasons = ALLOWED_REASON_NONE;
+ newBlockedReasons |= (isRestrictedByAdmin ? BLOCKED_METERED_REASON_ADMIN_DISABLED : 0);
+ newBlockedReasons |= (mRestrictBackground ? BLOCKED_METERED_REASON_DATA_SAVER : 0);
+ newBlockedReasons |= (isDenied ? BLOCKED_METERED_REASON_USER_RESTRICTED : 0);
+
+ newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
+ newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
+ newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0);
+
if (LOGV) {
Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
+ ": isForeground=" +isForeground
@@ -4616,6 +4699,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// Dispatch changed rule to existing listeners.
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+
+ final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+ if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+ mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+ uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+ .sendToTarget();
+ }
}
}
@@ -4690,6 +4785,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// Copy existing uid rules and clear ALL_NETWORK rules.
int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS);
+ UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ if (uidBlockedState == null) {
+ uidBlockedState = new UidBlockedState();
+ mUidBlockedState.put(uid, uidBlockedState);
+ }
+
// First step: define the new rule based on user restrictions and foreground state.
// NOTE: if statements below could be inlined, but it's easier to understand the logic
@@ -4702,6 +4803,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
}
+ int newBlockedReasons = BLOCKED_REASON_NONE;
+ int newAllowedReasons = ALLOWED_REASON_NONE;
+ newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0);
+ newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0);
+ newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
+ newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
+
+ newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
+ newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
+ newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
+ ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
+ newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
+ ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+
if (LOGV) {
Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
+ ", isIdle: " + isUidIdle
@@ -4733,6 +4848,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
}
+ final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+ if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+ mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+ uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+ .sendToTarget();
+ }
+
return newUidRules;
}
@@ -4762,61 +4889,57 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
- if (listener != null) {
- try {
- listener.onUidRulesChanged(uid, uidRules);
- } catch (RemoteException ignored) {
- }
+ try {
+ listener.onUidRulesChanged(uid, uidRules);
+ } catch (RemoteException ignored) {
}
}
private void dispatchMeteredIfacesChanged(INetworkPolicyListener listener,
String[] meteredIfaces) {
- if (listener != null) {
- try {
- listener.onMeteredIfacesChanged(meteredIfaces);
- } catch (RemoteException ignored) {
- }
+ try {
+ listener.onMeteredIfacesChanged(meteredIfaces);
+ } catch (RemoteException ignored) {
}
}
private void dispatchRestrictBackgroundChanged(INetworkPolicyListener listener,
boolean restrictBackground) {
- if (listener != null) {
- try {
- listener.onRestrictBackgroundChanged(restrictBackground);
- } catch (RemoteException ignored) {
- }
+ try {
+ listener.onRestrictBackgroundChanged(restrictBackground);
+ } catch (RemoteException ignored) {
}
}
private void dispatchUidPoliciesChanged(INetworkPolicyListener listener, int uid,
int uidPolicies) {
- if (listener != null) {
- try {
- listener.onUidPoliciesChanged(uid, uidPolicies);
- } catch (RemoteException ignored) {
- }
+ try {
+ listener.onUidPoliciesChanged(uid, uidPolicies);
+ } catch (RemoteException ignored) {
}
}
private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
int overrideMask, int overrideValue, int[] networkTypes) {
- if (listener != null) {
- try {
- listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
- } catch (RemoteException ignored) {
- }
+ try {
+ listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
+ } catch (RemoteException ignored) {
}
}
private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId,
SubscriptionPlan[] plans) {
- if (listener != null) {
- try {
- listener.onSubscriptionPlansChanged(subId, plans);
- } catch (RemoteException ignored) {
- }
+ try {
+ listener.onSubscriptionPlansChanged(subId, plans);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ private void dispatchBlockedReasonChanged(INetworkPolicyListener listener, int uid,
+ int oldBlockedReasons, int newBlockedReasons) {
+ try {
+ listener.onBlockedReasonChanged(uid, oldBlockedReasons, newBlockedReasons);
+ } catch (RemoteException ignored) {
}
}
@@ -4973,6 +5096,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mListeners.finishBroadcast();
return true;
}
+ case MSG_BLOCKED_REASON_CHANGED: {
+ final int uid = msg.arg1;
+ final int newBlockedReasons = msg.arg2;
+ final int oldBlockedReasons = (int) msg.obj;
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ dispatchBlockedReasonChanged(listener, uid,
+ oldBlockedReasons, newBlockedReasons);
+ }
+ mListeners.finishBroadcast();
+ return true;
+ }
default: {
return false;
}
@@ -5704,6 +5840,51 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
}
+ private class UidBlockedState {
+ public int blockedReasons;
+ public int allowedReasons;
+ public int effectiveBlockedReasons;
+
+ UidBlockedState() {
+ blockedReasons = BLOCKED_REASON_NONE;
+ allowedReasons = ALLOWED_REASON_NONE;
+ effectiveBlockedReasons = BLOCKED_REASON_NONE;
+ }
+
+ void updateEffectiveBlockedReasons() {
+ effectiveBlockedReasons = blockedReasons;
+ // If the uid is not subject to any blocked reasons, then return early
+ if (blockedReasons == BLOCKED_REASON_NONE) {
+ return;
+ }
+ if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) {
+ effectiveBlockedReasons = BLOCKED_REASON_NONE;
+ }
+ if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
+ effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE;
+ effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+ effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
+ effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_USER_RESTRICTED;
+ }
+ if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_ALLOWLIST) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
+ effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE;
+ effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+ }
+ if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
+ effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+ }
+ if ((allowedReasons & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
+ }
+ if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
+ }
+ }
+ }
+
private class NotificationId {
private final String mTag;
private final int mId;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6f39fea5dd95..e58836659189 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -134,7 +134,6 @@ import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
-import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
@@ -6082,6 +6081,17 @@ public class NotificationManagerService extends SystemService {
}
}
+ // Ensure CallStyle has all the correct actions
+ if ("android.app.Notification$CallStyle".equals(
+ notification.extras.getString(Notification.EXTRA_TEMPLATE))) {
+ Notification.Builder builder =
+ Notification.Builder.recoverBuilder(getContext(), notification);
+ Notification.CallStyle style = (Notification.CallStyle) builder.getStyle();
+ List<Notification.Action> actions = style.getActionsListWithSystemActions();
+ notification.actions = new Notification.Action[actions.size()];
+ actions.toArray(notification.actions);
+ }
+
// Remote views? Are they too big?
checkRemoteViews(pkg, tag, id, notification);
}
@@ -7366,15 +7376,7 @@ public class NotificationManagerService extends SystemService {
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(record.getKey()) != null) {
- // Vibrator checks the appops for the op package, not the caller,
- // so we need to add the bypass dnd flag to be heard. it's ok to
- // always add this flag here because we've already checked that we can
- // bypass dnd
- AudioAttributes.Builder aab =
- new AudioAttributes.Builder(record.getAudioAttributes())
- .setFlags(FLAG_BYPASS_INTERRUPTION_POLICY);
- mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
- effect, "Notification (delayed)", aab.build());
+ vibrate(record, effect, true);
} else {
Slog.e(TAG, "No vibration for canceled notification : "
+ record.getKey());
@@ -7382,8 +7384,7 @@ public class NotificationManagerService extends SystemService {
}
}).start();
} else {
- mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(),
- effect, "Notification", record.getAudioAttributes());
+ vibrate(record, effect, false);
}
return true;
} finally{
@@ -7391,6 +7392,16 @@ public class NotificationManagerService extends SystemService {
}
}
+ private void vibrate(NotificationRecord record, VibrationEffect effect, boolean delayed) {
+ // We need to vibrate as "android" so we can breakthrough DND. VibratorManagerService
+ // doesn't have a concept of vibrating on an app's behalf, so add the app information
+ // to the reason so we can still debug from bugreports
+ String reason = "Notification (" + record.getSbn().getOpPkg() + " "
+ + record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
+ mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
+ effect, reason, record.getAudioAttributes());
+ }
+
private boolean isNotificationForCurrentUser(NotificationRecord record) {
final int currentUser;
final long token = Binder.clearCallingIdentity();
@@ -10624,17 +10635,17 @@ public class NotificationManagerService extends SystemService {
return true;
}
}
- String toastMessage = "Indirect activity start from " + packageName;
String logcatMessage =
"Indirect notification activity start (trampoline) from " + packageName;
-
+ // Call to toast() method is posted to mHandler below to offload PM lookup from the
+ // activity start path
if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) {
- toast(toastMessage + " blocked.");
+ mHandler.post(() -> toast(packageName, uid, /* blocked */ true));
Slog.e(TAG, logcatMessage + " blocked");
return false;
} else {
if (mPackagesShown.add(packageName)) {
- toast(toastMessage + ". This will be blocked in S.");
+ mHandler.post(() -> toast(packageName, uid, /* blocked */ false));
}
Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
return true;
@@ -10650,10 +10661,19 @@ public class NotificationManagerService extends SystemService {
&& !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
}
- private void toast(String message) {
- mUiHandler.post(() ->
- Toast.makeText(getUiContext(), message + "\nSee g.co/dev/trampolines.",
- Toast.LENGTH_LONG).show());
+ private void toast(String packageName, int uid, boolean blocked) {
+ final CharSequence label;
+ try {
+ label = mPackageManagerClient.getApplicationLabel(
+ mPackageManager.getApplicationInfo(packageName, 0,
+ UserHandle.getUserId(uid)));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unexpected exception obtaining app label from PackageManager", e);
+ return;
+ }
+ mUiHandler.post(() -> Toast.makeText(getUiContext(),
+ label + " launch " + (blocked ? "blocked" : "will be blocked")
+ + "\ng.co/dev/trampolines", Toast.LENGTH_LONG).show());
}
}
}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 66ea55401cef..afce23fe7647 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -59,8 +59,8 @@ import android.util.apk.SignatureNotFoundException;
import android.util.apk.VerityBuilder;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.security.VerityUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.security.VerityUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 1acbabda9e19..ca8202f5f94b 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -105,6 +105,12 @@ public class AppsFilter implements Watchable, Snappable {
private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
/**
+ * A mapping from the set of App IDs that query other App IDs via library name to the
+ * list of packages that they can see.
+ */
+ private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>();
+
+ /**
* Executor for running reasonably short background tasks such as building the initial
* visibility cache.
*/
@@ -239,6 +245,7 @@ public class AppsFilter implements Watchable, Snappable {
Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable);
Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage);
Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent);
+ Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary);
mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
mForceQueryable.addAll(orig.mForceQueryable);
mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames;
@@ -508,6 +515,22 @@ public class AppsFilter implements Watchable, Snappable {
return false;
}
+ private static boolean canQueryViaUsesLibrary(AndroidPackage querying,
+ AndroidPackage potentialTarget) {
+ if (potentialTarget.getLibraryNames().isEmpty()) {
+ return false;
+ }
+ final List<String> libNames = potentialTarget.getLibraryNames();
+ for (int i = 0, size = libNames.size(); i < size; i++) {
+ final String libName = libNames.get(i);
+ if (querying.getUsesLibraries().contains(libName)
+ || querying.getUsesOptionalLibraries().contains(libName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static boolean matchesProviders(
Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
@@ -707,6 +730,9 @@ public class AppsFilter implements Watchable, Snappable {
|| canQueryAsInstaller(existingSetting, newPkg)) {
mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
+ if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+ mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId);
+ }
}
// now we'll evaluate our new package's ability to see existing packages
if (!mForceQueryable.contains(existingSetting.appId)) {
@@ -718,6 +744,9 @@ public class AppsFilter implements Watchable, Snappable {
|| canQueryAsInstaller(newPkgSetting, existingPkg)) {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
}
+ if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+ mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId);
+ }
}
// if either package instruments the other, mark both as visible to one another
if (newPkgSetting.pkg != null && existingSetting.pkg != null
@@ -1035,6 +1064,10 @@ public class AppsFilter implements Watchable, Snappable {
for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
}
+ mQueryableViaUsesLibrary.remove(setting.appId);
+ for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
+ mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId);
+ }
mForceQueryable.remove(setting.appId);
@@ -1315,6 +1348,18 @@ public class AppsFilter implements Watchable, Snappable {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
+ if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "queryable for library users");
+ }
+ return false;
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
return true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -1394,6 +1439,8 @@ public class AppsFilter implements Watchable, Snappable {
filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
mImplicitlyQueryable, " ", expandPackages);
}
+ pw.println(" queryable via uses-library:");
+ dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages);
}
private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId,
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 4be509b3f464..1d556fec31ea 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1492,7 +1492,7 @@ public class ComponentResolver {
}
return null;
}
- final ResolveInfo res = new ResolveInfo();
+ final ResolveInfo res = new ResolveInfo(info.hasCategory(Intent.CATEGORY_BROWSABLE));
res.activityInfo = ai;
if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index b34611b9cd6f..29322e2553e9 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -180,6 +180,8 @@ public class DataLoaderManagerService extends SystemService {
mId = id;
mListener = listener;
mDataLoader = null;
+
+ callListener(IDataLoaderStatusListener.DATA_LOADER_BINDING);
}
@Override
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d3a56c6f67c0..044e186140a4 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -70,6 +70,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -114,6 +115,7 @@ public class LauncherAppsService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
+ mLauncherAppsImpl.registerLoadingProgressForIncrementalApps();
}
static class BroadcastCookie {
@@ -1184,6 +1186,30 @@ public class LauncherAppsService extends SystemService {
mCallbackHandler.post(r);
}
+ /**
+ * Check all installed apps and if a package is installed via Incremental and not fully
+ * loaded, register loading progress listener.
+ */
+ void registerLoadingProgressForIncrementalApps() {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ final List<UserHandle> users = mUm.getUserProfiles();
+ if (users == null) {
+ return;
+ }
+ for (UserHandle user : users) {
+ pmInt.forEachInstalledPackage(pkg -> {
+ final String packageName = pkg.getPackageName();
+ if (pmInt.getIncrementalStatesInfo(packageName, Process.myUid(),
+ user.getIdentifier()).isLoading()) {
+ pmInt.registerInstalledLoadingProgressCallback(packageName,
+ new PackageLoadingProgressCallback(packageName, user),
+ user.getIdentifier());
+ }
+ }, user.getIdentifier());
+ }
+ }
+
public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
private final UserManagerInternal mUserManagerInternal;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0a443f3fd7f9..7aaab0c4d40b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -51,6 +51,7 @@ import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.StorageManager;
@@ -62,6 +63,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.ArtStatsLogUtils;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
@@ -99,6 +102,8 @@ public class PackageDexOptimizer {
private final PowerManager.WakeLock mDexoptWakeLock;
private volatile boolean mSystemReady;
+ private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
+
PackageDexOptimizer(Installer installer, Object installLock, Context context,
String wakeLockTag) {
this.mInstaller = installer;
@@ -252,6 +257,28 @@ public class PackageDexOptimizer {
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
packageStats, options.isDowngrade(), profileName, dexMetadataPath,
options.getCompilationReason());
+
+ // Only report metrics for base apk for now.
+ // TODO: add ISA and APK type to metrics.
+ if (pkg.getBaseApkPath().equals(path)) {
+ Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
+ try {
+ long sessionId = Math.randomLongInternal();
+ ArtStatsLogUtils.writeStatsLog(
+ mArtStatsLogger,
+ sessionId,
+ path,
+ compilerFilter,
+ sharedGid,
+ packageStats.getCompileTime(path),
+ dexMetadataPath,
+ options.getCompilationReason(),
+ newResult);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 903652ab76a5..bafe987cb546 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -138,6 +138,7 @@ import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.os.SomeArgs;
+import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -146,7 +147,6 @@ import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.security.VerityUtils;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -1011,8 +1011,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"DataLoader installation of APEX modules is not allowed.");
}
- if (this.params.dataLoaderParams.getComponentName().getPackageName()
- == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission(
+ boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+ this.params.dataLoaderParams.getComponentName().getPackageName());
+ if (systemDataLoader && mContext.checkCallingOrSelfPermission(
Manifest.permission.USE_SYSTEM_DATA_LOADERS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("You need the "
@@ -3060,7 +3061,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on
// supported on older devices.
maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile,
- VerityUtils.isFsVeritySupported() && DexMetadataHelper.isFsVerityRequired());
+ DexMetadataHelper.isFsVerityRequired());
}
private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
@@ -3653,6 +3654,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void onStatusChanged(int dataLoaderId, int status) {
switch (status) {
+ case IDataLoaderStatusListener.DATA_LOADER_BINDING:
case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
return;
@@ -3763,8 +3765,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
- final boolean systemDataLoader =
- params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
+ final boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+ params.getComponentName().getPackageName());
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a1da241c6642..1530e41917c7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -349,6 +349,7 @@ import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.AttributeCache;
+import com.android.internal.security.VerityUtils;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -401,7 +402,6 @@ import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
import com.android.server.rollback.RollbackManagerInternal;
-import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -410,6 +410,7 @@ import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -892,7 +893,7 @@ public class PackageManagerService extends IPackageManager.Stub
// that created the isolated process.
@Watched
@GuardedBy("mLock")
- final SparseIntArray mIsolatedOwners = new SparseIntArray();
+ final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -1767,6 +1768,11 @@ public class PackageManagerService extends IPackageManager.Stub
public int[] getAllUserIds() {
return mUserManager.getUserIds();
}
+
+ @Override
+ public boolean doesUserExist(@UserIdInt int userId) {
+ return mUserManager.exists(userId);
+ }
}
/**
@@ -1795,7 +1801,7 @@ public class PackageManagerService extends IPackageManager.Stub
public static final int SNAPPED = 2;
public final Settings settings;
- public final SparseIntArray isolatedOwners;
+ public final WatchedSparseIntArray isolatedOwners;
public final WatchedArrayMap<String, AndroidPackage> packages;
public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs;
public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs;
@@ -1814,7 +1820,7 @@ public class PackageManagerService extends IPackageManager.Stub
Snapshot(int type) {
if (type == Snapshot.SNAPPED) {
settings = mSettings.snapshot();
- isolatedOwners = mIsolatedOwners.clone();
+ isolatedOwners = mIsolatedOwners.snapshot();
packages = mPackages.snapshot();
sharedLibs = mSharedLibraries.snapshot();
staticLibs = mStaticLibsByDeclaringPackage.snapshot();
@@ -1979,6 +1985,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Nullable ComponentName component, @ComponentType int componentType, int userId);
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
int userId);
+ boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
+ int userId);
int bestDomainVerificationStatus(int status1, int status2);
int checkUidPermission(String permName, int uid);
int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
@@ -2014,7 +2022,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Cached attributes. The names in this class are the same as the
// names in PackageManagerService; see that class for documentation.
private final Settings mSettings;
- private final SparseIntArray mIsolatedOwners;
+ private final WatchedSparseIntArray mIsolatedOwners;
private final WatchedArrayMap<String, AndroidPackage> mPackages;
private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
mInstrumentation;
@@ -2636,8 +2644,7 @@ public class PackageManagerService extends IPackageManager.Stub
// We'll want to include browser possibilities in a few cases
boolean includeBrowser = false;
- if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
- matchFlags)) {
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
result.addAll(undefinedList);
// Maybe add one for the other profile.
if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
@@ -3549,7 +3556,7 @@ public class PackageManagerService extends IPackageManager.Stub
public String getInstantAppPackageName(int callingUid) {
// If the caller is an isolated app use the owner's uid for the lookup.
if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
+ callingUid = getIsolatedOwner(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
final Object obj = mSettings.getSettingLPr(appId);
@@ -3561,6 +3568,19 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
+ /**
+ * Finds the owner for the provided isolated UID. Throws IllegalStateException if no such
+ * isolated UID is found.
+ */
+ private int getIsolatedOwner(int isolatedUid) {
+ final int ownerUid = mIsolatedOwners.get(isolatedUid, -1);
+ if (ownerUid == -1) {
+ throw new IllegalStateException(
+ "No owner UID found for isolated UID " + isolatedUid);
+ }
+ return ownerUid;
+ }
+
public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
if (pkg.getStaticSharedLibName() != null) {
return pkg.getManifestPackageName();
@@ -3927,7 +3947,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
int callingUid) {
if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
+ callingUid = getIsolatedOwner(callingUid);
}
final PackageSetting ps = mSettings.getPackageLPr(packageName);
final boolean returnAllowed =
@@ -4081,7 +4101,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
+ callingUid = getIsolatedOwner(callingUid);
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final boolean callerIsInstantApp = instantAppPkgName != null;
@@ -4143,6 +4163,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
+ * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ */
+ public boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
+ int userId) {
+ boolean filterApp = true;
+ for (int index = sus.packages.size() - 1; index >= 0 && filterApp; index--) {
+ filterApp &= shouldFilterApplicationLocked(sus.packages.valueAt(index),
+ callingUid, /* component */ null, TYPE_UNKNOWN, userId);
+ }
+ return filterApp;
+ }
+
+ /**
* Verification statuses are ordered from the worse to the best, except for
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
*/
@@ -6149,6 +6182,7 @@ public class PackageManagerService extends IPackageManager.Stub
mAppsFilter.registerObserver(mWatcher);
mInstantAppRegistry.registerObserver(mWatcher);
mSettings.registerObserver(mWatcher);
+ mIsolatedOwners.registerObserver(mWatcher);
// If neither "build" attribute is true then this may be a mockito test, and verification
// can fail as a false positive.
Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
@@ -7904,6 +7938,15 @@ public class PackageManagerService extends IPackageManager.Stub
ps, callingUid, userId);
}
+ /**
+ * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ */
+ @GuardedBy("mLock")
+ private boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
+ int userId) {
+ return liveComputer().shouldFilterApplicationLocked(sus, callingUid, userId);
+ }
+
@GuardedBy("mLock")
private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags) {
@@ -8970,7 +9013,6 @@ public class PackageManagerService extends IPackageManager.Stub
public int checkUidSignatures(int uid1, int uid2) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
- final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
// Map to base uids.
final int appId1 = UserHandle.getAppId(uid1);
final int appId2 = UserHandle.getAppId(uid2);
@@ -8981,10 +9023,11 @@ public class PackageManagerService extends IPackageManager.Stub
Object obj = mSettings.getSettingLPr(appId1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
- if (isCallerInstantApp) {
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- p1SigningDetails = ((SharedUserSetting) obj).signatures.mSigningDetails;
+ p1SigningDetails = sus.signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
@@ -9000,10 +9043,11 @@ public class PackageManagerService extends IPackageManager.Stub
obj = mSettings.getSettingLPr(appId2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
- if (isCallerInstantApp) {
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- p2SigningDetails = ((SharedUserSetting) obj).signatures.mSigningDetails;
+ p2SigningDetails = sus.signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
@@ -9092,11 +9136,11 @@ public class PackageManagerService extends IPackageManager.Stub
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
- final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
- if (isCallerInstantApp) {
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
return false;
}
- signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
+ signingDetails = sus.signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
@@ -9221,16 +9265,19 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
+ final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
+ return null;
+ }
return sus.name + ":" + sus.userId;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplicationLocked(
- ps, callingUid, UserHandle.getUserId(callingUid))) {
+ if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return null;
}
return ps.name;
@@ -9248,6 +9295,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
+ final int callingUserId = UserHandle.getUserId(callingUid);
final String[] names = new String[uids.length];
synchronized (mLock) {
for (int i = uids.length - 1; i >= 0; i--) {
@@ -9255,11 +9303,14 @@ public class PackageManagerService extends IPackageManager.Stub
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
- names[i] = "shared:" + sus.name;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
+ names[i] = null;
+ } else {
+ names[i] = "shared:" + sus.name;
+ }
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplicationLocked(
- ps, callingUid, UserHandle.getUserId(callingUid))) {
+ if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
names[i] = null;
} else {
names[i] = ps.name;
@@ -9301,16 +9352,19 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
+ final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
+ return 0;
+ }
return sus.pkgFlags;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplicationLocked(
- ps, callingUid, UserHandle.getUserId(callingUid))) {
+ if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return 0;
}
return ps.pkgFlags;
@@ -9325,16 +9379,19 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
+ final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
synchronized (mLock) {
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
+ return 0;
+ }
return sus.pkgPrivateFlags;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplicationLocked(
- ps, callingUid, UserHandle.getUserId(callingUid))) {
+ if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return 0;
}
return ps.pkgPrivateFlags;
@@ -25640,6 +25697,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) {
mPermissionManager.onUserCreated(userId);
mLegacyPermissionManager.grantDefaultPermissions(userId);
+ mDomainVerificationManager.clearUser(userId);
}
}
@@ -27765,13 +27823,23 @@ public class PackageManagerService extends IPackageManager.Stub
}
private static String getDefaultTimeouts() {
- return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
- PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private static String getKnownDigestersList() {
- return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
- PROPERTY_KNOWN_DIGESTERS_LIST, "");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ PROPERTY_KNOWN_DIGESTERS_LIST, "");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d1cf55de7254..38cba4ca9e20 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1889,231 +1889,248 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public void setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId, @NonNull AndroidFuture callback) {
- verifyCaller(packageName, userId);
+ try {
+ verifyCaller(packageName, userId);
- final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
- verifyShortcutInfoPackages(packageName, newShortcuts);
- final int size = newShortcuts.size();
+ final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+ verifyShortcutInfoPackages(packageName, newShortcuts);
+ final int size = newShortcuts.size();
- final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
- injectBinderCallingPid(), injectBinderCallingUid());
+ final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+ injectBinderCallingPid(), injectBinderCallingUid());
- List<ShortcutInfo> changedShortcuts = null;
- List<ShortcutInfo> removedShortcuts = null;
+ List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
- synchronized (mLock) {
- throwIfUserLockedL(userId);
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
- final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+ final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName,
+ userId);
- ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
- ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+ ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
- fillInDefaultActivity(newShortcuts);
+ fillInDefaultActivity(newShortcuts);
- ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
+ ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
- // Throttling.
- if (!ps.tryApiCall(unlimited)) {
- callback.complete(false);
- }
+ // Throttling.
+ if (!ps.tryApiCall(unlimited)) {
+ callback.complete(false);
+ return;
+ }
- // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
- ps.clearAllImplicitRanks();
- assignImplicitRanks(newShortcuts);
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ assignImplicitRanks(newShortcuts);
- for (int i = 0; i < size; i++) {
- fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
- }
+ for (int i = 0; i < size; i++) {
+ fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
+ }
- ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
- ps.findAll(cachedOrPinned, (ShortcutInfo si) -> si.isVisibleToPublisher()
- && si.isDynamic() && (si.isCached() || si.isPinned()),
- ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+ ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
+ ps.findAll(cachedOrPinned, (ShortcutInfo si) -> si.isVisibleToPublisher()
+ && si.isDynamic() && (si.isCached() || si.isPinned()),
+ ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- // First, remove all un-pinned and non-cached; dynamic shortcuts
- removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ // First, remove all un-pinned and non-cached; dynamic shortcuts
+ removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
- // Then, add/update all. We need to make sure to take over "pinned" flag.
- for (int i = 0; i < size; i++) {
- final ShortcutInfo newShortcut = newShortcuts.get(i);
- ps.addOrReplaceDynamicShortcut(newShortcut);
- }
+ // Then, add/update all. We need to make sure to take over "pinned" flag.
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo newShortcut = newShortcuts.get(i);
+ ps.addOrReplaceDynamicShortcut(newShortcut);
+ }
- // Lastly, adjust the ranks.
- ps.adjustRanks();
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
- changedShortcuts = prepareChangedShortcuts(
- cachedOrPinned, newShortcuts, removedShortcuts, ps);
- }
+ changedShortcuts = prepareChangedShortcuts(
+ cachedOrPinned, newShortcuts, removedShortcuts, ps);
+ }
- packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
- verifyStates();
+ verifyStates();
- callback.complete(true);
+ callback.complete(true);
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
+ }
}
@Override
public void updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId, AndroidFuture callback) {
- verifyCaller(packageName, userId);
+ try {
+ verifyCaller(packageName, userId);
- final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
- verifyShortcutInfoPackages(packageName, newShortcuts);
- final int size = newShortcuts.size();
+ final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+ verifyShortcutInfoPackages(packageName, newShortcuts);
+ final int size = newShortcuts.size();
- final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
- injectBinderCallingPid(), injectBinderCallingUid());
+ final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+ injectBinderCallingPid(), injectBinderCallingUid());
- final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
+ final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
- synchronized (mLock) {
- throwIfUserLockedL(userId);
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
- final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+ final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName,
+ userId);
- ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
- ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+ ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
- // For update, don't fill in the default activity. Having null activity means
- // "don't update the activity" here.
+ // For update, don't fill in the default activity. Having null activity means
+ // "don't update the activity" here.
- ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
+ ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
- // Throttling.
- if (!ps.tryApiCall(unlimited)) {
- callback.complete(false);
- return;
- }
+ // Throttling.
+ if (!ps.tryApiCall(unlimited)) {
+ callback.complete(false);
+ return;
+ }
- // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
- ps.clearAllImplicitRanks();
- assignImplicitRanks(newShortcuts);
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ assignImplicitRanks(newShortcuts);
- for (int i = 0; i < size; i++) {
- final ShortcutInfo source = newShortcuts.get(i);
- fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo source = newShortcuts.get(i);
+ fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
- ps.mutateShortcut(source.getId(), null, target -> {
- // Invisible shortcuts can't be updated.
- if (target == null || !target.isVisibleToPublisher()) {
- return;
- }
+ ps.mutateShortcut(source.getId(), null, target -> {
+ // Invisible shortcuts can't be updated.
+ if (target == null || !target.isVisibleToPublisher()) {
+ return;
+ }
- if (target.isEnabled() != source.isEnabled()) {
- Slog.w(TAG,
- "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
- }
+ if (target.isEnabled() != source.isEnabled()) {
+ Slog.w(TAG, "ShortcutInfo.enabled cannot be changed with"
+ + " updateShortcuts()");
+ }
- if (target.isLongLived() != source.isLongLived()) {
- Slog.w(TAG,
- "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
- }
+ if (target.isLongLived() != source.isLongLived()) {
+ Slog.w(TAG,
+ "ShortcutInfo.longLived cannot be changed with"
+ + " updateShortcuts()");
+ }
- // When updating the rank, we need to insert between existing ranks, so set
- // this setRankChanged, and also copy the implicit rank fo adjustRanks().
- if (source.hasRank()) {
- target.setRankChanged();
- target.setImplicitRank(source.getImplicitRank());
- }
+ // When updating the rank, we need to insert between existing ranks, so set
+ // this setRankChanged, and also copy the implicit rank fo adjustRanks().
+ if (source.hasRank()) {
+ target.setRankChanged();
+ target.setImplicitRank(source.getImplicitRank());
+ }
- final boolean replacingIcon = (source.getIcon() != null);
- if (replacingIcon) {
- removeIconLocked(target);
- }
+ final boolean replacingIcon = (source.getIcon() != null);
+ if (replacingIcon) {
+ removeIconLocked(target);
+ }
- // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
- target.copyNonNullFieldsFrom(source);
- target.setTimestamp(injectCurrentTimeMillis());
+ // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
+ target.copyNonNullFieldsFrom(source);
+ target.setTimestamp(injectCurrentTimeMillis());
- if (replacingIcon) {
- saveIconAndFixUpShortcutLocked(target);
- }
+ if (replacingIcon) {
+ saveIconAndFixUpShortcutLocked(target);
+ }
- // When we're updating any resource related fields, re-extract the res names and
- // the values.
- if (replacingIcon || source.hasStringResources()) {
- fixUpShortcutResourceNamesAndValues(target);
- }
+ // When we're updating any resource related fields, re-extract the res
+ // names and the values.
+ if (replacingIcon || source.hasStringResources()) {
+ fixUpShortcutResourceNamesAndValues(target);
+ }
- changedShortcuts.add(target);
- });
- }
+ changedShortcuts.add(target);
+ });
+ }
- // Lastly, adjust the ranks.
- ps.adjustRanks();
- }
- packageShortcutsChanged(packageName, userId,
- changedShortcuts.isEmpty() ? null : changedShortcuts, null);
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
+ }
+ packageShortcutsChanged(packageName, userId,
+ changedShortcuts.isEmpty() ? null : changedShortcuts, null);
- verifyStates();
+ verifyStates();
- callback.complete(true);
+ callback.complete(true);
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
+ }
}
@Override
public void addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId, AndroidFuture callback) {
- verifyCaller(packageName, userId);
+ try {
+ verifyCaller(packageName, userId);
- final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
- verifyShortcutInfoPackages(packageName, newShortcuts);
- final int size = newShortcuts.size();
+ final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+ verifyShortcutInfoPackages(packageName, newShortcuts);
+ final int size = newShortcuts.size();
- final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
- injectBinderCallingPid(), injectBinderCallingUid());
+ final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+ injectBinderCallingPid(), injectBinderCallingUid());
- List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> changedShortcuts = null;
- synchronized (mLock) {
- throwIfUserLockedL(userId);
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
- final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+ final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName,
+ userId);
- ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
- ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+ ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
- fillInDefaultActivity(newShortcuts);
+ fillInDefaultActivity(newShortcuts);
- ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
+ ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
- // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
- ps.clearAllImplicitRanks();
- assignImplicitRanks(newShortcuts);
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ assignImplicitRanks(newShortcuts);
- // Throttling.
- if (!ps.tryApiCall(unlimited)) {
- callback.complete(false);
- return;
- }
- for (int i = 0; i < size; i++) {
- final ShortcutInfo newShortcut = newShortcuts.get(i);
+ // Throttling.
+ if (!ps.tryApiCall(unlimited)) {
+ callback.complete(false);
+ return;
+ }
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo newShortcut = newShortcuts.get(i);
- // Validate the shortcut.
- fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
+ // Validate the shortcut.
+ fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
- // When ranks are changing, we need to insert between ranks, so set the
- // "rank changed" flag.
- newShortcut.setRankChanged();
+ // When ranks are changing, we need to insert between ranks, so set the
+ // "rank changed" flag.
+ newShortcut.setRankChanged();
- // Add it.
- ps.addOrReplaceDynamicShortcut(newShortcut);
+ // Add it.
+ ps.addOrReplaceDynamicShortcut(newShortcut);
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(newShortcut);
}
- changedShortcuts.add(newShortcut);
- }
- // Lastly, adjust the ranks.
- ps.adjustRanks();
- }
- packageShortcutsChanged(packageName, userId, changedShortcuts, null);
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
+ }
+ packageShortcutsChanged(packageName, userId, changedShortcuts, null);
- verifyStates();
+ verifyStates();
- callback.complete(true);
+ callback.complete(true);
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
+ }
}
@Override
@@ -2180,31 +2197,40 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public void requestPinShortcut(String packageName, ShortcutInfo shortcut,
IntentSender resultIntent, int userId, AndroidFuture callback) {
- Objects.requireNonNull(shortcut);
- Objects.requireNonNull(callback);
- Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
- callback.complete(requestPinItem(packageName, userId, shortcut, null, null, resultIntent));
+ try {
+ Objects.requireNonNull(shortcut);
+ Objects.requireNonNull(callback);
+ Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+ callback.complete(
+ requestPinItem(packageName, userId, shortcut, null, null, resultIntent));
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
+ }
}
@Override
public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId,
AndroidFuture callback)
throws RemoteException {
- Objects.requireNonNull(shortcut);
- Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
- verifyCaller(packageName, userId);
- verifyShortcutInfoPackage(packageName, shortcut);
+ try {
+ Objects.requireNonNull(shortcut);
+ Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+ verifyCaller(packageName, userId);
+ verifyShortcutInfoPackage(packageName, shortcut);
- final Intent ret;
- synchronized (mLock) {
- throwIfUserLockedL(userId);
+ final Intent ret;
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
- // Send request to the launcher, if supported.
- ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
- }
+ // Send request to the launcher, if supported.
+ ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
+ }
- verifyStates();
- callback.complete(ret);
+ verifyStates();
+ callback.complete(ret);
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
+ }
}
/**
@@ -2464,47 +2490,57 @@ public class ShortcutService extends IShortcutService.Stub {
public void getShortcuts(String packageName,
@ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId,
AndroidFuture<ParceledListSlice<ShortcutInfo>> callback) {
- verifyCaller(packageName, userId);
-
- synchronized (mLock) {
- throwIfUserLockedL(userId);
-
- final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
- final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
- final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
- final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
+ try {
+ verifyCaller(packageName, userId);
- final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
- | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
- | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
- | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
- callback.complete(getShortcutsWithQueryLocked(
- packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- (ShortcutInfo si) ->
- si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0));
+ final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
+ final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
+ final boolean matchManifest =
+ (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
+ final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
+
+ final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
+ | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
+ | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
+ | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
+
+ callback.complete(getShortcutsWithQueryLocked(
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+ (ShortcutInfo si) ->
+ si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0));
+ }
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
}
}
@Override
public void getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId,
AndroidFuture<ParceledListSlice> callback) {
- Preconditions.checkStringNotEmpty(packageName, "packageName");
- Objects.requireNonNull(filter, "intentFilter");
+ try {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Objects.requireNonNull(filter, "intentFilter");
- verifyCaller(packageName, userId);
- enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
- "getShareTargets");
+ verifyCaller(packageName, userId);
+ enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
+ "getShareTargets");
- synchronized (mLock) {
- throwIfUserLockedL(userId);
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
- final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
+ final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
- final ShortcutUser user = getUserShortcutsLocked(userId);
- user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.forAllPackages(
+ p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
- callback.complete(new ParceledListSlice<>(shortcutInfoList));
+ callback.complete(new ParceledListSlice<>(shortcutInfoList));
+ }
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
}
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
new file mode 100644
index 000000000000..0c8e36b75425
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+import android.util.jar.StrictJarFile;
+import android.util.Slog;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.PackageManagerService;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+
+/** Utils class to report ART metrics to statsd. */
+public class ArtStatsLogUtils {
+ private static final String TAG = ArtStatsLogUtils.class.getSimpleName();
+ private static final String PROFILE_DEX_METADATA = "primary.prof";
+ private static final String VDEX_DEX_METADATA = "primary.vdex";
+
+
+ private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY =
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+ private static final int ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED =
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+ private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED =
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+
+ private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK =
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+ private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK =
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+ private static final Map<Integer, Integer> COMPILATION_REASON_MAP = new HashMap();
+
+ static {
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_UNKNOWN, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_FAST, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_FAST);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+ ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+ ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+ ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BACKGROUND_DEXOPT, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BG_DEXOPT);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_AB_OTA, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_AB_OTA);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,
+ ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INACTIVE);
+ COMPILATION_REASON_MAP.put(PackageManagerService.REASON_SHARED,
+ ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_SHARED);
+ }
+
+ private static final Map<String, Integer> COMPILE_FILTER_MAP = new HashMap();
+
+ static {
+ COMPILE_FILTER_MAP.put("error", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR);
+ COMPILE_FILTER_MAP.put("unknown", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN);
+ COMPILE_FILTER_MAP.put("assume-verified", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED);
+ COMPILE_FILTER_MAP.put("extract", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT);
+ COMPILE_FILTER_MAP.put("verify", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY);
+ COMPILE_FILTER_MAP.put("quicken", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_QUICKEN);
+ COMPILE_FILTER_MAP.put("space-profile", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE);
+ COMPILE_FILTER_MAP.put("space", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE);
+ COMPILE_FILTER_MAP.put("speed-profile", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE);
+ COMPILE_FILTER_MAP.put("speed", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED);
+ COMPILE_FILTER_MAP.put("everything-profile", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE);
+ COMPILE_FILTER_MAP.put("everything", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING);
+ COMPILE_FILTER_MAP.put("run-from-apk", ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK);
+ COMPILE_FILTER_MAP.put("run-from-apk-fallback",
+ ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK);
+ COMPILE_FILTER_MAP.put("run-from-vdex-fallback",
+ ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK);
+ }
+
+ public static void writeStatsLog(
+ ArtStatsLogger logger,
+ long sessionId,
+ String path,
+ String compilerFilter,
+ int uid,
+ long compileTime,
+ String dexMetadataPath,
+ int compilationReason,
+ int result) {
+ int dexMetadataType = getDexMetadataType(dexMetadataPath);
+ logger.write(
+ sessionId,
+ uid,
+ compilationReason,
+ compilerFilter,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+ result,
+ dexMetadataType);
+ logger.write(
+ sessionId,
+ uid,
+ compilationReason,
+ compilerFilter,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+ getDexBytes(path),
+ dexMetadataType);
+ logger.write(
+ sessionId,
+ uid,
+ compilationReason,
+ compilerFilter,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+ compileTime,
+ dexMetadataType);
+ }
+
+ private static long getDexBytes(String apkPath) {
+ StrictJarFile jarFile = null;
+ long dexBytes = 0;
+ try {
+ jarFile = new StrictJarFile(apkPath,
+ /*verify=*/ false,
+ /*signatureSchemeRollbackProtectionsEnforced=*/ false);
+ Iterator<ZipEntry> it = jarFile.iterator();
+ while (it.hasNext()) {
+ ZipEntry entry = it.next();
+ if (entry.getName().matches("classes(\\d)*[.]dex")) {
+ dexBytes += entry.getSize();
+ }
+ }
+ return dexBytes;
+ } catch (IOException ignore) {
+ Slog.e(TAG, "Error when parsing APK " + apkPath);
+ return -1L;
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ private static int getDexMetadataType(String dexMetadataPath) {
+ if (dexMetadataPath == null) {
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE;
+ }
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(dexMetadataPath,
+ /*verify=*/ false,
+ /*signatureSchemeRollbackProtectionsEnforced=*/false);
+ boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA);
+ boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA);
+ if (hasProfile && hasVdex) {
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX;
+ } else if (hasProfile) {
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE;
+ } else if (hasVdex) {
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX;
+ } else {
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN;
+ }
+ } catch (IOException ignore) {
+ Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath);
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_ERROR;
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ private static boolean findFileName(StrictJarFile jarFile, String filename) throws IOException {
+ Iterator<ZipEntry> it = jarFile.iterator();
+ while (it.hasNext()) {
+ ZipEntry entry = it.next();
+ if (entry.getName().equals(filename)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static class ArtStatsLogger {
+ public void write(
+ long sessionId,
+ int uid,
+ int compilationReason,
+ String compilerFilter,
+ int kind,
+ long value,
+ int dexMetadataType) {
+ ArtStatsLog.write(
+ ArtStatsLog.ART_DATUM_REPORTED,
+ sessionId,
+ uid,
+ COMPILE_FILTER_MAP.getOrDefault(compilerFilter, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
+ COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
+ /*timestamp_millis=*/ 0L,
+ ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
+ kind,
+ value,
+ dexMetadataType);
+ }
+ }
+}
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 349561d3f1d1..37f317557aeb 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -986,7 +986,7 @@ public class DexManager {
* Fetches the battery manager object and caches it if it hasn't been fetched already.
*/
private BatteryManager getBatteryManager() {
- if (mBatteryManager == null) {
+ if (mBatteryManager == null && mContext != null) {
mBatteryManager = mContext.getSystemService(BatteryManager.class);
}
@@ -1008,10 +1008,6 @@ public class DexManager {
&& mPowerManager.getCurrentThermalStatus()
>= PowerManager.THERMAL_STATUS_SEVERE);
- if (DEBUG) {
- Log.d(TAG, "Battery, thermal, or memory are critical: " + isBtmCritical);
- }
-
return isBtmCritical;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e3ccb7521b58..27bf8a13766a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -737,10 +737,12 @@ final class DefaultPermissionGrantPolicy {
}
}
if (locationExtraPackageNames != null) {
- // Also grant location permission to location extra packages.
+ // Also grant location and activity recognition permission to location extra packages.
for (String packageName : locationExtraPackageNames) {
grantPermissionsToSystemPackage(pm, packageName, userId,
ALWAYS_LOCATION_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
+ ACTIVITY_RECOGNITION_PERMISSIONS);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index b61fd8d633f6..39ed4882c69c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
-import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -151,7 +150,7 @@ public class DomainVerificationDebug {
Integer state = reusedMap.valueAt(stateIndex);
writer.print(domain);
writer.print(": ");
- writer.println(DomainVerificationManager.stateToDebugString(state));
+ writer.println(DomainVerificationState.stateToDebugString(state));
}
writer.decreaseIndent();
writer.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index 1721a18f4f60..f4bcd3e65913 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -141,6 +141,12 @@ public class DomainVerificationEnforcer {
"Caller is not allowed to edit other users");
}
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
@@ -161,6 +167,12 @@ public class DomainVerificationEnforcer {
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user selections");
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
if (packageName == null) {
return true;
}
@@ -184,6 +196,12 @@ public class DomainVerificationEnforcer {
}
}
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
@@ -197,6 +215,12 @@ public class DomainVerificationEnforcer {
"Caller is not allowed to edit other users");
}
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
@@ -221,6 +245,12 @@ public class DomainVerificationEnforcer {
mContext.enforcePermission(
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
callingPid, callingUid, "Caller is not allowed to query user selections");
+
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
}
public interface Callback {
@@ -229,5 +259,7 @@ public class DomainVerificationEnforcer {
* if the package was not installed
*/
boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
+
+ boolean doesUserExist(@UserIdInt int userId);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 0c2b4c547dae..73182732cdaf 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -80,14 +80,14 @@ public interface DomainVerificationManagerInternal {
* been preserved for migration purposes, but is otherwise ignored. Corresponds to
* {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS}.
*/
- int APPROVAL_LEVEL_LEGACY_ALWAYS = 1;
+ int APPROVAL_LEVEL_LEGACY_ALWAYS = 2;
/**
* The app has been chosen by the user through
* {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
* indicating an explicit choice to use this app to open an unverified domain.
*/
- int APPROVAL_LEVEL_SELECTION = 2;
+ int APPROVAL_LEVEL_SELECTION = 3;
/**
* The app is approved through the digital asset link statement being hosted at the domain
@@ -95,7 +95,7 @@ public interface DomainVerificationManagerInternal {
* {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by
* the domain verification agent on device.
*/
- int APPROVAL_LEVEL_VERIFIED = 3;
+ int APPROVAL_LEVEL_VERIFIED = 4;
/**
* The app has been installed as an instant app, which grants it total authority on the domains
@@ -105,7 +105,7 @@ public interface DomainVerificationManagerInternal {
* The user is still able to disable instant app link handling through
* {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}.
*/
- int APPROVAL_LEVEL_INSTANT_APP = 4;
+ int APPROVAL_LEVEL_INSTANT_APP = 5;
/**
* Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)}
@@ -333,10 +333,10 @@ public interface DomainVerificationManagerInternal {
@Nullable
UUID getDomainVerificationInfoId(@NonNull String packageName);
+ @DomainVerificationManager.Error
@RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
- void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
- @NonNull Set<String> domains, int state)
- throws IllegalArgumentException, NameNotFoundException;
+ int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+ @NonNull Set<String> domains, int state) throws NameNotFoundException;
interface Connection extends DomainVerificationEnforcer.Callback {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index a7a52e0cd10c..2a17c6d4cec5 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -24,7 +24,6 @@ import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
import android.os.ServiceSpecificException;
@@ -61,11 +60,12 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
}
}
+ @DomainVerificationManager.Error
@Override
- public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
+ public int setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
int state) {
try {
- mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
+ return mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
domainSet.getDomains(), state);
} catch (Exception e) {
throw rethrow(e);
@@ -82,11 +82,12 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
}
}
+ @DomainVerificationManager.Error
@Override
- public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
+ public int setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
boolean enabled, @UserIdInt int userId) {
try {
- mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
+ return mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
domainSet.getDomains(), enabled, userId);
} catch (Exception e) {
throw rethrow(e);
@@ -116,14 +117,9 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
}
private RuntimeException rethrow(Exception exception) throws RuntimeException {
- if (exception instanceof InvalidDomainSetException) {
- int packedErrorCode = DomainVerificationManager.ERROR_INVALID_DOMAIN_SET;
- packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
- return new ServiceSpecificException(packedErrorCode,
- ((InvalidDomainSetException) exception).getPackageName());
- } else if (exception instanceof NameNotFoundException) {
+ if (exception instanceof NameNotFoundException) {
return new ServiceSpecificException(
- DomainVerificationManager.ERROR_NAME_NOT_FOUND);
+ DomainVerificationManager.INTERNAL_ERROR_NAME_NOT_FOUND);
} else if (exception instanceof RuntimeException) {
return (RuntimeException) exception;
} else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index e85bbe41f747..a0e252a8a28a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -19,6 +19,7 @@ package com.android.server.pm.verify.domain;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
+import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -34,7 +35,6 @@ import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationState;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
@@ -243,17 +243,17 @@ public class DomainVerificationService extends SystemService
throws NameNotFoundException {
mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
synchronized (mLock) {
- DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
- if (pkgState == null) {
- return null;
- }
-
AndroidPackage pkg = mConnection.getPackageLocked(packageName);
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- Map<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ return null;
+ }
+
+ ArrayMap<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
// TODO(b/159952358): Should the domain list be cached?
ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
@@ -267,44 +267,56 @@ public class DomainVerificationService extends SystemService
DomainVerificationState.STATE_NO_RESPONSE);
}
+ final int mapSize = hostToStateMap.size();
+ for (int index = 0; index < mapSize; index++) {
+ int internalValue = hostToStateMap.valueAt(index);
+ int publicValue = DomainVerificationState.convertToInfoState(internalValue);
+ hostToStateMap.setValueAt(index, publicValue);
+ }
+
// TODO(b/159952358): Do not return if no values are editable (all ignored states)?
return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
}
}
- public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
- int state) throws InvalidDomainSetException, NameNotFoundException {
+ @DomainVerificationManager.Error
+ public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ int state) throws NameNotFoundException {
if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
if (state != DomainVerificationState.STATE_SUCCESS) {
- throw new IllegalArgumentException(
- "Verifier can only set STATE_SUCCESS or codes greater than or equal to "
- + "STATE_FIRST_VERIFIER_DEFINED");
+ return DomainVerificationManager.ERROR_INVALID_STATE_CODE;
}
}
- setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId, domains,
- state);
+ return setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId,
+ domains, state);
}
+ @DomainVerificationManager.Error
@Override
- public void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+ public int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
@NonNull Set<String> domains, int state)
- throws InvalidDomainSetException, NameNotFoundException {
+ throws NameNotFoundException {
mEnforcer.assertApprovedVerifier(callingUid, mProxy);
synchronized (mLock) {
List<String> verifiedDomains = new ArrayList<>();
- DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+ GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
true /* forAutoVerify */, callingUid, null /* userId */);
+ if (result.isError()) {
+ return result.getErrorCode();
+ }
+
+ DomainVerificationPkgState pkgState = result.getPkgState();
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
for (String domain : domains) {
Integer previousState = stateMap.get(domain);
if (previousState != null
- && !DomainVerificationManager.isStateModifiable(previousState)) {
+ && !DomainVerificationState.isModifiable(previousState)) {
continue;
}
- if (DomainVerificationManager.isStateVerified(state)) {
+ if (DomainVerificationState.isVerified(state)) {
verifiedDomains.add(domain);
}
@@ -318,6 +330,7 @@ public class DomainVerificationService extends SystemService
}
mConnection.scheduleWriteSettings();
+ return DomainVerificationManager.STATUS_OK;
}
@Override
@@ -460,17 +473,24 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- pkgState.getOrCreateUserState(userId)
- .setLinkHandlingAllowed(allowed);
+ if (userId == UserHandle.USER_ALL) {
+ for (int aUserId : mConnection.getAllUserIds()) {
+ pkgState.getOrCreateUserState(aUserId)
+ .setLinkHandlingAllowed(allowed);
+ }
+ } else {
+ pkgState.getOrCreateUserState(userId)
+ .setLinkHandlingAllowed(allowed);
+ }
}
}
mConnection.scheduleWriteSettings();
}
- public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
- throws InvalidDomainSetException, NameNotFoundException {
+ throws NameNotFoundException {
synchronized (mLock) {
final int callingUid = mConnection.getCallingUid();
// Pass null for package name here and do the app visibility enforcement inside
@@ -478,14 +498,17 @@ public class DomainVerificationService extends SystemService
// ID reason if the target app is invisible
if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
null /* packageName */, userId)) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_ID_INVALID);
+ return DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID;
}
- DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+ GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
false /* forAutoVerify */, callingUid, userId);
- DomainVerificationInternalUserState userState =
- pkgState.getOrCreateUserState(userId);
+ if (result.isError()) {
+ return result.getErrorCode();
+ }
+
+ DomainVerificationPkgState pkgState = result.getPkgState();
+ DomainVerificationInternalUserState userState = pkgState.getOrCreateUserState(userId);
// Disable other packages if approving this one. Note that this check is only done for
// enabling. This allows an escape hatch in case multiple packages somehow get selected.
@@ -503,8 +526,7 @@ public class DomainVerificationService extends SystemService
userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked);
int highestApproval = packagesToLevel.second;
if (highestApproval > APPROVAL_LEVEL_SELECTION) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_UNABLE_TO_APPROVE);
+ return DomainVerificationManager.ERROR_UNABLE_TO_APPROVE;
}
domainToApprovedPackages.put(domain, packagesToLevel.first);
@@ -544,6 +566,7 @@ public class DomainVerificationService extends SystemService
}
mConnection.scheduleWriteSettings();
+ return DomainVerificationManager.STATUS_OK;
}
@Override
@@ -636,16 +659,16 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
synchronized (mLock) {
- DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
- if (pkgState == null) {
- return null;
- }
-
AndroidPackage pkg = mConnection.getPackageLocked(packageName);
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ return null;
+ }
+
ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg);
int webDomainsSize = webDomains.size();
@@ -659,7 +682,7 @@ public class DomainVerificationService extends SystemService
Integer state = stateMap.get(host);
int domainState;
- if (state != null && DomainVerificationManager.isStateVerified(state)) {
+ if (state != null && DomainVerificationState.isVerified(state)) {
domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
} else if (enabledHosts.contains(host)) {
domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
@@ -793,19 +816,12 @@ public class DomainVerificationService extends SystemService
Integer oldStateInteger = oldStateMap.get(domain);
if (oldStateInteger != null) {
int oldState = oldStateInteger;
- switch (oldState) {
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_RESTORED:
- case DomainVerificationState.STATE_MIGRATED:
- newStateMap.put(domain, oldState);
- break;
- default:
- // In all other cases, the state code is left unset
- // (STATE_NO_RESPONSE) to signal to the verification agent that any
- // existing error has been cleared and the domain should be
- // re-attempted. This makes update of a package a signal to
- // re-verify.
- break;
+ // If the following case fails, the state code is left unset
+ // (STATE_NO_RESPONSE) to signal to the verification agent that any existing
+ // error has been cleared and the domain should be re-attempted. This makes
+ // update of a package a signal to re-verify.
+ if (DomainVerificationState.shouldMigrate(oldState)) {
+ newStateMap.put(domain, oldState);
}
}
}
@@ -858,13 +874,13 @@ public class DomainVerificationService extends SystemService
boolean sendBroadcast = true;
DomainVerificationPkgState pkgState;
- pkgState = mSettings.getPendingState(pkgName);
+ pkgState = mSettings.removePendingState(pkgName);
if (pkgState != null) {
// Don't send when attaching from pending read, which is usually boot scan. Re-send on
// boot is handled in a separate method once all packages are added.
sendBroadcast = false;
} else {
- pkgState = mSettings.getRestoredState(pkgName);
+ pkgState = mSettings.removeRestoredState(pkgName);
}
AndroidPackage pkg = newPkgSetting.getPkg();
@@ -872,7 +888,7 @@ public class DomainVerificationService extends SystemService
boolean hasAutoVerifyDomains = !domains.isEmpty();
boolean isPendingOrRestored = pkgState != null;
if (isPendingOrRestored) {
- pkgState.setId(domainSetId);
+ pkgState = new DomainVerificationPkgState(pkgState, domainSetId, hasAutoVerifyDomains);
} else {
pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
}
@@ -1097,28 +1113,25 @@ public class DomainVerificationService extends SystemService
* @param userIdForFilter which user to filter app access to, or null if the caller has already
* validated package visibility
*/
+ @CheckResult
@GuardedBy("mLock")
- private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
+ private GetAttachedResult getAndValidateAttachedLocked(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
- @Nullable Integer userIdForFilter)
- throws InvalidDomainSetException, NameNotFoundException {
+ @Nullable Integer userIdForFilter) throws NameNotFoundException {
if (domainSetId == null) {
- throw new InvalidDomainSetException(null, null,
- InvalidDomainSetException.REASON_ID_NULL);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL);
}
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(domainSetId);
if (pkgState == null) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_ID_INVALID);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
}
String pkgName = pkgState.getPackageName();
if (userIdForFilter != null
&& mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_ID_INVALID);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
}
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
@@ -1127,8 +1140,8 @@ public class DomainVerificationService extends SystemService
}
if (CollectionUtils.isEmpty(domains)) {
- throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
- InvalidDomainSetException.REASON_SET_NULL_OR_EMPTY);
+ return GetAttachedResult.error(
+ DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY);
}
AndroidPackage pkg = pkgSetting.getPkg();
ArraySet<String> declaredDomains = forAutoVerify
@@ -1136,11 +1149,10 @@ public class DomainVerificationService extends SystemService
: mCollector.collectAllWebDomains(pkg);
if (domains.retainAll(declaredDomains)) {
- throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
- InvalidDomainSetException.REASON_UNKNOWN_DOMAIN);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN);
}
- return pkgState;
+ return GetAttachedResult.success(pkgState);
}
@Override
@@ -1185,7 +1197,7 @@ public class DomainVerificationService extends SystemService
/**
* Determine whether or not a broadcast should be sent at boot for the given {@param pkgState}.
* Sends only if the only states recorded are default as decided by {@link
- * DomainVerificationManager#isStateDefault(int)}.
+ * DomainVerificationState#isDefault(int)}.
*
* If any other state is set, it's assumed that the domain verification agent is aware of the
* package and has already scheduled future verification requests.
@@ -1199,7 +1211,7 @@ public class DomainVerificationService extends SystemService
int statesSize = stateMap.size();
for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
Integer state = stateMap.valueAt(stateIndex);
- if (!DomainVerificationManager.isStateDefault(state)) {
+ if (!DomainVerificationState.isDefault(state)) {
return false;
}
}
@@ -1318,83 +1330,50 @@ public class DomainVerificationService extends SystemService
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
String domain = intent.getData().getHost();
- // Collect package names
- ArrayMap<String, Integer> packageApprovals = new ArrayMap<>();
+ // Collect valid infos
+ ArrayMap<ResolveInfo, Integer> infoApprovals = new ArrayMap<>();
int infosSize = infos.size();
for (int index = 0; index < infosSize; index++) {
- packageApprovals.put(infos.get(index).getComponentInfo().packageName,
- APPROVAL_LEVEL_NONE);
+ final ResolveInfo info = infos.get(index);
+ // Only collect for intent filters that can auto resolve
+ if (info.isAutoResolutionAllowed()) {
+ infoApprovals.put(info, null);
+ }
}
// Find all approval levels
- int highestApproval = fillMapWithApprovalLevels(packageApprovals, domain, userId,
+ int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId,
pkgSettingFunction);
if (highestApproval == APPROVAL_LEVEL_NONE) {
return Pair.create(emptyList(), highestApproval);
}
- // Filter to highest, non-zero packages
- ArraySet<String> approvedPackages = new ArraySet<>();
- int approvalsSize = packageApprovals.size();
- for (int index = 0; index < approvalsSize; index++) {
- if (packageApprovals.valueAt(index) == highestApproval) {
- approvedPackages.add(packageApprovals.keyAt(index));
+ // Filter to highest, non-zero infos
+ for (int index = infoApprovals.size() - 1; index >= 0; index--) {
+ if (infoApprovals.valueAt(index) != highestApproval) {
+ infoApprovals.removeAt(index);
}
}
- ArraySet<String> filteredPackages = new ArraySet<>();
- if (highestApproval == APPROVAL_LEVEL_LEGACY_ASK) {
+ if (highestApproval != APPROVAL_LEVEL_LEGACY_ASK) {
// To maintain legacy behavior while the Settings API is not implemented,
// show the chooser if all approved apps are marked ask, skipping the
// last app, last declaration filtering.
- filteredPackages.addAll(approvedPackages);
- } else {
- // Filter to last installed package
- long latestInstall = Long.MIN_VALUE;
- int approvedSize = approvedPackages.size();
- for (int index = 0; index < approvedSize; index++) {
- String packageName = approvedPackages.valueAt(index);
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
- if (pkgSetting == null) {
- continue;
- }
- long installTime = pkgSetting.getFirstInstallTime();
- if (installTime > latestInstall) {
- latestInstall = installTime;
- filteredPackages.clear();
- filteredPackages.add(packageName);
- } else if (installTime == latestInstall) {
- filteredPackages.add(packageName);
- }
- }
+ filterToLastFirstInstalled(infoApprovals, pkgSettingFunction);
}
- // Filter to approved ResolveInfos
- ArrayMap<String, List<ResolveInfo>> approvedInfos = new ArrayMap<>();
- for (int index = 0; index < infosSize; index++) {
- ResolveInfo info = infos.get(index);
- String packageName = info.getComponentInfo().packageName;
- if (filteredPackages.contains(packageName)) {
- List<ResolveInfo> infosPerPackage = approvedInfos.get(packageName);
- if (infosPerPackage == null) {
- infosPerPackage = new ArrayList<>();
- approvedInfos.put(packageName, infosPerPackage);
- }
- infosPerPackage.add(info);
- }
+ // Easier to transform into list as the filterToLastDeclared method
+ // requires swapping indexes, which doesn't work with ArrayMap keys
+ final int size = infoApprovals.size();
+ List<ResolveInfo> finalList = new ArrayList<>(size);
+ for (int index = 0; index < size; index++) {
+ finalList.add(infoApprovals.keyAt(index));
}
- List<ResolveInfo> finalList;
- if (highestApproval == APPROVAL_LEVEL_LEGACY_ASK) {
- // If legacy ask, skip the last declaration filtering
- finalList = new ArrayList<>();
- int size = approvedInfos.size();
- for (int index = 0; index < size; index++) {
- finalList.addAll(approvedInfos.valueAt(index));
- }
- } else {
+ // If legacy ask, skip the last declaration filtering
+ if (highestApproval != APPROVAL_LEVEL_LEGACY_ASK) {
// Find the last declared ResolveInfo per package
- finalList = filterToLastDeclared(approvedInfos, pkgSettingFunction);
+ filterToLastDeclared(finalList, pkgSettingFunction);
}
return Pair.create(finalList, highestApproval);
@@ -1403,68 +1382,127 @@ public class DomainVerificationService extends SystemService
/**
* @return highest approval level found
*/
- private int fillMapWithApprovalLevels(@NonNull ArrayMap<String, Integer> inputMap,
+ @ApprovalLevel
+ private int fillMapWithApprovalLevels(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
@NonNull String domain, @UserIdInt int userId,
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
int highestApproval = APPROVAL_LEVEL_NONE;
int size = inputMap.size();
for (int index = 0; index < size; index++) {
- String packageName = inputMap.keyAt(index);
+ if (inputMap.valueAt(index) != null) {
+ // Already filled by previous iteration
+ continue;
+ }
+
+ ResolveInfo info = inputMap.keyAt(index);
+ final String packageName = info.getComponentInfo().packageName;
PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
- inputMap.setValueAt(index, APPROVAL_LEVEL_NONE);
+ fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE);
continue;
}
int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain);
highestApproval = Math.max(highestApproval, approval);
- inputMap.setValueAt(index, approval);
+ fillInfoMapForSamePackage(inputMap, packageName, approval);
}
return highestApproval;
}
+ private void fillInfoMapForSamePackage(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
+ @NonNull String targetPackageName, @ApprovalLevel int level) {
+ final int size = inputMap.size();
+ for (int index = 0; index < size; index++) {
+ final String packageName = inputMap.keyAt(index).getComponentInfo().packageName;
+ if (Objects.equals(targetPackageName, packageName)) {
+ inputMap.setValueAt(index, level);
+ }
+ }
+ }
+
@NonNull
- private List<ResolveInfo> filterToLastDeclared(
- @NonNull ArrayMap<String, List<ResolveInfo>> inputMap,
+ private void filterToLastFirstInstalled(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
- List<ResolveInfo> finalList = new ArrayList<>(inputMap.size());
-
- int inputSize = inputMap.size();
- for (int inputIndex = 0; inputIndex < inputSize; inputIndex++) {
- String packageName = inputMap.keyAt(inputIndex);
- List<ResolveInfo> infos = inputMap.valueAt(inputIndex);
+ // First, find the package with the latest first install time
+ String targetPackageName = null;
+ long latestInstall = Long.MIN_VALUE;
+ final int size = inputMap.size();
+ for (int index = 0; index < size; index++) {
+ ResolveInfo info = inputMap.keyAt(index);
+ String packageName = info.getComponentInfo().packageName;
PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ if (pkgSetting == null) {
+ continue;
+ }
+
+ long installTime = pkgSetting.getFirstInstallTime();
+ if (installTime > latestInstall) {
+ latestInstall = installTime;
+ targetPackageName = packageName;
+ }
+ }
+
+ // Then, remove all infos that don't match the package
+ for (int index = inputMap.size() - 1; index >= 0; index--) {
+ ResolveInfo info = inputMap.keyAt(index);
+ if (!Objects.equals(targetPackageName, info.getComponentInfo().packageName)) {
+ inputMap.removeAt(index);
+ }
+ }
+ }
+
+ @NonNull
+ private void filterToLastDeclared(@NonNull List<ResolveInfo> inputList,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ // Must call size each time as the size of the list will decrease
+ for (int index = 0; index < inputList.size(); index++) {
+ ResolveInfo info = inputList.get(index);
+ String targetPackageName = info.getComponentInfo().packageName;
+ PackageSetting pkgSetting = pkgSettingFunction.apply(targetPackageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
continue;
}
- ResolveInfo result = null;
- int highestIndex = -1;
- int infosSize = infos.size();
- for (int infoIndex = 0; infoIndex < infosSize; infoIndex++) {
- ResolveInfo info = infos.get(infoIndex);
- List<ParsedActivity> activities = pkg.getActivities();
- int activitiesSize = activities.size();
- for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
- if (Objects.equals(activities.get(activityIndex).getComponentName(),
- info.getComponentInfo().getComponentName())) {
- if (activityIndex > highestIndex) {
- highestIndex = activityIndex;
- result = info;
- }
- break;
- }
+ ResolveInfo result = info;
+ int highestIndex = indexOfIntentFilterEntry(pkg, result);
+
+ // Search backwards so that lower results can be removed as they're found
+ for (int searchIndex = inputList.size() - 1; searchIndex >= index + 1; searchIndex--) {
+ ResolveInfo searchInfo = inputList.get(searchIndex);
+ if (!Objects.equals(targetPackageName, searchInfo.getComponentInfo().packageName)) {
+ continue;
}
+
+ int entryIndex = indexOfIntentFilterEntry(pkg, searchInfo);
+ if (entryIndex > highestIndex) {
+ highestIndex = entryIndex;
+ result = searchInfo;
+ }
+
+ // Always remove the entry so that the current index
+ // is left as the sole candidate of the target package
+ inputList.remove(searchIndex);
}
- // Shouldn't be null, but might as well be safe
- if (result != null) {
- finalList.add(result);
+ // Swap the current index for the result, leaving this as
+ // the only entry with the target package name
+ inputList.set(index, result);
+ }
+ }
+
+ private int indexOfIntentFilterEntry(@NonNull AndroidPackage pkg,
+ @NonNull ResolveInfo target) {
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+ if (Objects.equals(activities.get(activityIndex).getComponentName(),
+ target.getComponentInfo().getComponentName())) {
+ return activityIndex;
}
}
- return finalList;
+ return -1;
}
@Override
@@ -1472,8 +1510,7 @@ public class DomainVerificationService extends SystemService
@NonNull List<ResolveInfo> candidates,
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
String packageName = pkgSetting.getName();
- if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
- resolveInfoFlags)) {
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, intent, userId, false, "not valid intent");
}
@@ -1542,7 +1579,7 @@ public class DomainVerificationService extends SystemService
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
// Check if the exact host matches
Integer state = stateMap.get(host);
- if (state != null && DomainVerificationManager.isStateVerified(state)) {
+ if (state != null && DomainVerificationState.isVerified(state)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, debugObject, userId, true,
"host verified exactly");
@@ -1553,7 +1590,7 @@ public class DomainVerificationService extends SystemService
// Otherwise see if the host matches a verified domain by wildcard
int stateMapSize = stateMap.size();
for (int index = 0; index < stateMapSize; index++) {
- if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) {
+ if (!DomainVerificationState.isVerified(stateMap.valueAt(index))) {
continue;
}
@@ -1672,4 +1709,40 @@ public class DomainVerificationService extends SystemService
Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for "
+ debugObject + " for user " + userId + ": " + reason);
}
+
+ private static class GetAttachedResult {
+
+ @Nullable
+ private DomainVerificationPkgState mPkgState;
+
+ private int mErrorCode;
+
+ GetAttachedResult(@Nullable DomainVerificationPkgState pkgState, int errorCode) {
+ mPkgState = pkgState;
+ mErrorCode = errorCode;
+ }
+
+ @NonNull
+ static GetAttachedResult error(@DomainVerificationManager.Error int errorCode) {
+ return new GetAttachedResult(null, errorCode);
+ }
+
+ @NonNull
+ static GetAttachedResult success(@NonNull DomainVerificationPkgState pkgState) {
+ return new GetAttachedResult(pkgState, DomainVerificationManager.STATUS_OK);
+ }
+
+ @NonNull
+ DomainVerificationPkgState getPkgState() {
+ return mPkgState;
+ }
+
+ boolean isError() {
+ return mErrorCode != DomainVerificationManager.STATUS_OK;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index f3d1dbb1f6ad..8b59da7bb944 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -256,16 +256,16 @@ class DomainVerificationSettings {
}
@Nullable
- public DomainVerificationPkgState getPendingState(@NonNull String pkgName) {
+ public DomainVerificationPkgState removePendingState(@NonNull String pkgName) {
synchronized (mLock) {
- return mPendingPkgStates.get(pkgName);
+ return mPendingPkgStates.remove(pkgName);
}
}
@Nullable
- public DomainVerificationPkgState getRestoredState(@NonNull String pkgName) {
+ public DomainVerificationPkgState removeRestoredState(@NonNull String pkgName) {
synchronized (mLock) {
- return mRestoredPkgStates.get(pkgName);
+ return mRestoredPkgStates.remove(pkgName);
}
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index 94767f555574..7e755fa384db 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -38,6 +38,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
public class DomainVerificationShell {
@@ -226,22 +228,19 @@ public class DomainVerificationShell {
userId = translateUserId(userId, "runSetAppLinksUserState");
- String enabledString = commandHandler.getNextArgRequired();
+ String enabledArg = commandHandler.getNextArg();
+ if (TextUtils.isEmpty(enabledArg)) {
+ commandHandler.getErrPrintWriter().println("Error: enabled param not specified");
+ return false;
+ }
- // Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
- // accidentally parsed as a boolean
boolean enabled;
- switch (enabledString) {
- case "true":
- enabled = true;
- break;
- case "false":
- enabled = false;
- break;
- default:
- commandHandler.getErrPrintWriter().println(
- "Invalid enabled param: " + enabledString);
- return false;
+ try {
+ enabled = parseEnabled(enabledArg);
+ } catch (IllegalArgumentException e) {
+ commandHandler.getErrPrintWriter()
+ .println("Error: invalid enabled param: " + e.getMessage());
+ return false;
}
ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
@@ -255,8 +254,8 @@ public class DomainVerificationShell {
}
try {
- mCallback.setDomainVerificationUserSelectionInternal(userId,
- packageName, enabled, domains);
+ mCallback.setDomainVerificationUserSelectionInternal(userId, packageName, enabled,
+ domains);
} catch (NameNotFoundException e) {
commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
return false;
@@ -362,15 +361,12 @@ public class DomainVerificationShell {
private boolean runSetAppLinksAllowed(@NonNull BasicShellCommandHandler commandHandler) {
String packageName = null;
Integer userId = null;
- Boolean allowed = null;
String option;
while ((option = commandHandler.getNextOption()) != null) {
if (option.equals("--package")) {
- packageName = commandHandler.getNextArgRequired();
- } if (option.equals("--user")) {
+ packageName = commandHandler.getNextArg();
+ } else if (option.equals("--user")) {
userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
- } else if (allowed == null) {
- allowed = Boolean.valueOf(option);
} else {
commandHandler.getErrPrintWriter().println("Error: unexpected option: " + option);
return false;
@@ -389,11 +385,21 @@ public class DomainVerificationShell {
return false;
}
- if (allowed == null) {
+ String allowedArg = commandHandler.getNextArg();
+ if (TextUtils.isEmpty(allowedArg)) {
commandHandler.getErrPrintWriter().println("Error: allowed setting not specified");
return false;
}
+ boolean allowed;
+ try {
+ allowed = parseEnabled(allowedArg);
+ } catch (IllegalArgumentException e) {
+ commandHandler.getErrPrintWriter()
+ .println("Error: invalid allowed setting: " + e.getMessage());
+ return false;
+ }
+
userId = translateUserId(userId, "runSetAppLinksAllowed");
try {
@@ -422,6 +428,22 @@ public class DomainVerificationShell {
}
/**
+ * Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
+ * accidentally parsed as a boolean.
+ */
+ @NonNull
+ private boolean parseEnabled(@NonNull String arg) throws IllegalArgumentException {
+ switch (arg.toLowerCase(Locale.US)) {
+ case "true":
+ return true;
+ case "false":
+ return false;
+ default:
+ throw new IllegalArgumentException(arg + " is not a valid boolean");
+ }
+ }
+
+ /**
* Separated interface from {@link DomainVerificationManagerInternal} to hide methods that are
* even more internal, and so that testing is easier.
*/
@@ -498,7 +520,8 @@ public class DomainVerificationShell {
void verifyPackages(@Nullable List<String> packageNames, boolean reVerify);
/**
- * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer)
+ * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer,
+ * Function)
*/
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId) throws NameNotFoundException;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 883bbad1bd2d..cb3b5c9db7e7 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
import android.os.Binder;
import com.android.internal.util.CollectionUtils;
@@ -30,7 +29,6 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import java.util.List;
import java.util.Set;
public final class DomainVerificationUtils {
@@ -46,7 +44,6 @@ public final class DomainVerificationUtils {
}
public static boolean isDomainVerificationIntent(Intent intent,
- @NonNull List<ResolveInfo> candidates,
@PackageManager.ResolveInfoFlags int resolveInfoFlags) {
if (!intent.isWebIntent()) {
return false;
@@ -63,42 +60,18 @@ public final class DomainVerificationUtils {
&& intent.hasCategory(Intent.CATEGORY_BROWSABLE);
}
- // In cases where at least one browser is resolved and only one non-browser is resolved,
- // the Intent is coerced into an app links intent, under the assumption the browser can
- // be skipped if the app is approved at any level for the domain.
- boolean foundBrowser = false;
- boolean foundOneApp = false;
-
- final int candidatesSize = candidates.size();
- for (int index = 0; index < candidatesSize; index++) {
- final ResolveInfo info = candidates.get(index);
- if (info.handleAllWebDataURI) {
- foundBrowser = true;
- } else if (foundOneApp) {
- // Already true, so duplicate app
- foundOneApp = false;
- break;
- } else {
- foundOneApp = true;
- }
- }
-
boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
- boolean onlyOneNonBrowser = foundBrowser && foundOneApp;
// Check if matches (BROWSABLE || none) && DEFAULT
if (categoriesSize == 0) {
- // No categories, run coerce case, matching DEFAULT by flags
- return onlyOneNonBrowser && matchDefaultByFlags;
- } else if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) {
- // Run coerce case, matching by explicit DEFAULT
- return onlyOneNonBrowser;
+ // No categories, only allow matching DEFAULT by flags
+ return matchDefaultByFlags;
} else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
// Intent matches BROWSABLE, must match DEFAULT by flags
return matchDefaultByFlags;
} else {
- // Otherwise not matching any app link categories
- return false;
+ // Otherwise only needs to have DEFAULT
+ return intent.hasCategory(Intent.CATEGORY_DEFAULT);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index a089a6022735..40c70915f3ce 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -68,6 +68,12 @@ public class DomainVerificationPkgState {
this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
}
+ public DomainVerificationPkgState(@NonNull DomainVerificationPkgState pkgState,
+ @NonNull UUID id, boolean hasAutoVerifyDomains) {
+ this(pkgState.getPackageName(), id, hasAutoVerifyDomains, pkgState.getStateMap(),
+ pkgState.getUserStates());
+ }
+
@Nullable
public DomainVerificationInternalUserState getUserState(@UserIdInt int userId) {
return mUserStates.get(userId);
@@ -84,10 +90,6 @@ public class DomainVerificationPkgState {
return userState;
}
- public void setId(@NonNull UUID id) {
- mId = id;
- }
-
public void removeUser(@UserIdInt int userId) {
mUserStates.remove(userId);
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index 18042af139a3..fa36683e4aff 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -207,20 +207,24 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
int callingUid = response.callingUid;
if (!successfulDomains.isEmpty()) {
try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- successfulDomains, DomainVerificationState.STATE_SUCCESS);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException e) {
+ if (mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ successfulDomains, DomainVerificationState.STATE_SUCCESS)
+ != DomainVerificationManager.STATUS_OK) {
+ Slog.e(TAG, "Failure reporting successful domains for " + packageName);
+ }
+ } catch (Exception e) {
Slog.e(TAG, "Failure reporting successful domains for " + packageName, e);
}
}
if (!failedDomains.isEmpty()) {
try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException e) {
+ if (mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE)
+ != DomainVerificationManager.STATUS_OK) {
+ Slog.e(TAG, "Failure reporting failed domains for " + packageName);
+ }
+ } catch (Exception e) {
Slog.e(TAG, "Failure reporting failed domains for " + packageName, e);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 047e3b362b7a..d0aa28b441a0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -345,6 +345,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private final Object mLock = new Object();
+ /** List of {@link ScreenOnListener}s which do not belong to the default display. */
+ private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>();
+
Context mContext;
IWindowManager mWindowManager;
WindowManagerFuncs mWindowManagerFuncs;
@@ -434,8 +437,25 @@ public class PhoneWindowManager implements WindowManagerPolicy {
volatile boolean mBeganFromNonInteractive;
volatile boolean mEndCallKeyHandled;
volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
- volatile boolean mGoingToSleep;
- volatile boolean mRequestedOrGoingToSleep;
+
+ /**
+ * {@code true} if the device is entering a low-power state; {@code false otherwise}.
+ *
+ * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state
+ * of the {@link #mDefaultDisplay default display} versus the power state of the entire device.
+ */
+ volatile boolean mDeviceGoingToSleep;
+
+ /**
+ * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to
+ * enter a low-power state; {@code false otherwise}.
+ *
+ * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire
+ * device versus the power state of the {@link #mDefaultDisplay default display}.
+ */
+ // TODO(b/178103325): Track sleep/requested sleep for every display.
+ volatile boolean mRequestedOrSleepingDefaultDisplay;
+
volatile boolean mRecentsVisible;
volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
volatile boolean mPictureInPictureVisible;
@@ -917,13 +937,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
- goToSleepFromPowerButton(eventTime, 0);
+ sleepDefaultDisplayFromPowerButton(eventTime, 0);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
- goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+ sleepDefaultDisplayFromPowerButton(eventTime,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
- if (goToSleepFromPowerButton(eventTime,
+ if (sleepDefaultDisplayFromPowerButton(eventTime,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
launchHomeFromHotKey(DEFAULT_DISPLAY);
}
@@ -951,11 +972,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/**
- * Sends the device to sleep as a result of a power button press.
+ * Sends the default display to sleep as a result of a power button press.
*
- * @return True if the was device was sent to sleep, false if sleep was suppressed.
+ * @return {@code true} if the device was sent to sleep, {@code false} if the device did not
+ * sleep.
*/
- private boolean goToSleepFromPowerButton(long eventTime, int flags) {
+ private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
// Before we actually go to sleep, we check the last wakeup reason.
// If the device very recently woke up from a gesture (like user lifting their device)
// then ignore the sleep instruction. This is because users have developed
@@ -975,12 +997,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
+ sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
return true;
}
- private void goToSleep(long eventTime, int reason, int flags) {
- mRequestedOrGoingToSleep = true;
+ private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
+ mRequestedOrSleepingDefaultDisplay = true;
mPowerManager.goToSleep(eventTime, reason, flags);
}
@@ -1017,7 +1039,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Global.THEATER_MODE_ON, 1);
if (mGoToSleepOnButtonPressTheaterMode && interactive) {
- goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+ sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+ 0);
}
}
break;
@@ -1126,7 +1149,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
- goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+ sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
break;
}
}
@@ -2212,9 +2235,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+ public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+ int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+ int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
if (!SHOW_SPLASH_SCREENS) {
return null;
}
@@ -2241,10 +2264,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (theme != context.getThemeResId() || labelRes != 0) {
try {
- context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);
+ context = context.createPackageContextAsUser(packageName, CONTEXT_RESTRICTED,
+ UserHandle.of(userId));
context.setTheme(theme);
} catch (PackageManager.NameNotFoundException e) {
- // Ignore
+ Slog.w(TAG, "Failed creating package context with package name "
+ + packageName + " for user " + userId, e);
}
}
@@ -3511,7 +3536,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if ((mEndcallBehavior
& Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
- goToSleep(event.getEventTime(),
+ sleepDefaultDisplay(event.getEventTime(),
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
isWakeKey = false;
}
@@ -3538,10 +3563,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Any activity on the power button stops the accessibility shortcut
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
+ final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+ final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
if (down) {
- interceptPowerKeyDown(event, interactive);
+ interceptPowerKeyDown(event, interactiveAndOn);
} else {
- interceptPowerKeyUp(event, interactive, canceled);
+ interceptPowerKeyUp(event, interactiveAndOn, canceled);
}
break;
}
@@ -3746,7 +3773,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final MutableBoolean outLaunched = new MutableBoolean(false);
final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event,
interactive, outLaunched);
- if (outLaunched.value && mRequestedOrGoingToSleep) {
+ if (outLaunched.value && mRequestedOrSleepingDefaultDisplay) {
mCameraGestureTriggeredDuringGoingToSleep = true;
}
return gesturedServiceIntercepted;
@@ -4088,8 +4115,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pmSleepReason)) + ")");
}
- mGoingToSleep = true;
- mRequestedOrGoingToSleep = true;
+ mDeviceGoingToSleep = true;
+ mRequestedOrSleepingDefaultDisplay = true;
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
@@ -4108,8 +4135,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
- mGoingToSleep = false;
- mRequestedOrGoingToSleep = false;
+ mDeviceGoingToSleep = false;
+ mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(false);
// We must get this work done here because the power manager will drop
@@ -4224,12 +4251,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
mPowerManager.wakeUp(wakeTime, reason, details);
-
- // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
- final HdmiControl hdmiControl = getHdmiControl();
- if (hdmiControl != null) {
- hdmiControl.turnOnTv();
- }
return true;
}
@@ -4253,21 +4274,25 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurnedOff(int displayId) {
- if (displayId != DEFAULT_DISPLAY) {
- return;
- }
-
- if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
+ if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
- updateScreenOffSleepToken(true);
- mDefaultDisplayPolicy.screenTurnedOff();
- synchronized (mLock) {
- if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onScreenTurnedOff();
+ if (displayId == DEFAULT_DISPLAY) {
+ updateScreenOffSleepToken(true);
+ mRequestedOrSleepingDefaultDisplay = false;
+ mDefaultDisplayPolicy.screenTurnedOff();
+ synchronized (mLock) {
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.onScreenTurnedOff();
+ }
+ }
+ mDefaultDisplayRotation.updateOrientationListener();
+ reportScreenStateToVrManager(false);
+ if (mCameraGestureTriggeredDuringGoingToSleep) {
+ wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
+ PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+ "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
}
}
- mDefaultDisplayRotation.updateOrientationListener();
- reportScreenStateToVrManager(false);
}
private long getKeyguardDrawnTimeout() {
@@ -4280,27 +4305,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
- if (displayId != DEFAULT_DISPLAY) {
- return;
- }
-
- if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
+ if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");
- Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
- updateScreenOffSleepToken(false);
- mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
+ if (displayId == DEFAULT_DISPLAY) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
+ 0 /* cookie */);
+ updateScreenOffSleepToken(false);
+ mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
- synchronized (mLock) {
- if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
- mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
- getKeyguardDrawnTimeout());
- mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
- } else {
- if (DEBUG_WAKEUP) Slog.d(TAG,
- "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
- mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+ synchronized (mLock) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
+ mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
+ getKeyguardDrawnTimeout());
+ mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
+ } else {
+ if (DEBUG_WAKEUP) Slog.d(TAG,
+ "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
+ mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+ }
}
+ } else {
+ mScreenOnListeners.put(displayId, screenOnListener);
}
}
@@ -4321,11 +4347,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
+ mWindowManagerFuncs.screenTurningOff(displayId, screenOffListener);
if (displayId != DEFAULT_DISPLAY) {
return;
}
- mWindowManagerFuncs.screenTurningOff(screenOffListener);
+ mRequestedOrSleepingDefaultDisplay = true;
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurningOff();
@@ -4380,6 +4407,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
listener.onScreenOn();
}
+ for (int i = mScreenOnListeners.size() - 1; i >= 0; i--) {
+ final ScreenOnListener screenOnListener = mScreenOnListeners.valueAt(i);
+ if (screenOnListener != null) {
+ screenOnListener.onScreenOn();
+ }
+ }
+ mScreenOnListeners.clear();
+
if (enableScreen) {
try {
mWindowManager.enableScreenIfNeeded();
@@ -4410,7 +4445,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public boolean okToAnimate() {
- return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
+ return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep;
}
/** {@inheritDoc} */
@@ -4777,7 +4812,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs.lockDeviceNow();
break;
case LID_BEHAVIOR_SLEEP:
- goToSleep(SystemClock.uptimeMillis(),
+ sleepDefaultDisplay(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
index b9202c334fec..72933a0ad309 100644
--- a/services/core/java/com/android/server/policy/SplashScreenSurface.java
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -45,7 +45,7 @@ class SplashScreenSurface implements StartingSurface {
}
@Override
- public void remove() {
+ public void remove(boolean animate) {
if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": "
+ this + " Callers=" + Debug.getCallers(4));
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b5a9acacec83..d512edfb066a 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -238,8 +238,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Removes the starting window surface. Do not hold the window manager lock when calling
* this method!
+ * @param animate Whether need to play the default exit animation for starting window.
*/
- void remove();
+ void remove(boolean animate);
}
/**
@@ -303,9 +304,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Notifies the window manager that screen is being turned off.
*
+ * @param displayId the ID of the display which is turning off
* @param listener callback to call when display can be turned off
*/
- void screenTurningOff(ScreenOffListener listener);
+ void screenTurningOff(int displayId, ScreenOffListener listener);
/**
* Convert the lid state to a human readable format.
@@ -712,9 +714,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* @return The starting surface.
*
*/
- public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig, int displayId);
+ StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+ int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+ int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId);
/**
* Set or clear a window which can behave as the keyguard.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index a95628f633ad..44f14b4d5b0d 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -258,8 +258,12 @@ public class KeyguardServiceDelegate {
}
}
+ /**
+ * @deprecated Notify occlude status change via remote animation.
+ */
+ @Deprecated
public void setOccluded(boolean isOccluded, boolean animate) {
- if (mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
mKeyguardService.setOccluded(isOccluded, animate);
}
diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java
index 2442079bec8b..676181dcfb67 100644
--- a/services/core/java/com/android/server/power/FaceDownDetector.java
+++ b/services/core/java/com/android/server/power/FaceDownDetector.java
@@ -30,6 +30,7 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.Slog;
@@ -66,7 +67,7 @@ public class FaceDownDetector implements SensorEventListener {
private static final float MOVING_AVERAGE_WEIGHT = 0.5f;
/** DeviceConfig flag name, if {@code true}, enables Face Down features. */
- private static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
+ static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_FEATURE_ENABLED = true;
@@ -139,6 +140,7 @@ public class FaceDownDetector implements SensorEventListener {
new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT);
private boolean mFaceDown = false;
+ private boolean mInteractive = false;
private boolean mActive = false;
private float mPrevAcceleration = 0;
@@ -149,62 +151,64 @@ public class FaceDownDetector implements SensorEventListener {
private final Handler mHandler;
private final Runnable mUserActivityRunnable;
+ private final BroadcastReceiver mScreenReceiver;
+
+ private Context mContext;
public FaceDownDetector(@NonNull Consumer<Boolean> onFlip) {
mOnFlip = Objects.requireNonNull(onFlip);
mHandler = new Handler(Looper.getMainLooper());
+ mScreenReceiver = new ScreenStateReceiver();
mUserActivityRunnable = () -> {
if (mFaceDown) {
exitFaceDown(USER_INTERACTION, SystemClock.uptimeMillis() - mLastFlipTime);
- checkAndUpdateActiveState(false);
+ updateActiveState();
}
};
}
/** Initializes the FaceDownDetector and all necessary listeners. */
public void systemReady(Context context) {
+ mContext = context;
mSensorManager = context.getSystemService(SensorManager.class);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
readValuesFromDeviceConfig();
- checkAndUpdateActiveState(true);
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
ActivityThread.currentApplication().getMainExecutor(),
(properties) -> onDeviceConfigChange(properties.getKeyset()));
+ updateActiveState();
+ }
+
+ private void registerScreenReceiver(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiver(new ScreenStateReceiver(), intentFilter);
+ context.registerReceiver(mScreenReceiver, intentFilter);
}
/**
* Sets the active state of the detector. If false, we will not process accelerometer changes.
*/
- private void checkAndUpdateActiveState(boolean active) {
- if (mIsEnabled && mActive != active) {
- final long currentTime = SystemClock.uptimeMillis();
- // Don't make active if there was recently a user interaction while face down.
- if (active && mPreviousResultType == USER_INTERACTION
- && currentTime - mPreviousResultTime < mUserInteractionBackoffMillis) {
- return;
- }
- if (DEBUG) Slog.d(TAG, "Update active - " + active);
- mActive = active;
- if (!active) {
- if (mFaceDown && mPreviousResultTime != USER_INTERACTION) {
- mPreviousResultType = SCREEN_OFF_RESULT;
- mPreviousResultTime = currentTime;
+ private void updateActiveState() {
+ final long currentTime = SystemClock.uptimeMillis();
+ final boolean sawRecentInteraction = mPreviousResultType == USER_INTERACTION
+ && currentTime - mPreviousResultTime < mUserInteractionBackoffMillis;
+ final boolean shouldBeActive = mInteractive && mIsEnabled && !sawRecentInteraction;
+ if (mActive != shouldBeActive) {
+ if (shouldBeActive) {
+ mSensorManager.registerListener(
+ this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
+ if (mPreviousResultType == SCREEN_OFF_RESULT) {
+ logScreenOff();
}
+ } else {
mSensorManager.unregisterListener(this);
mFaceDown = false;
mOnFlip.accept(false);
- } else {
- if (mPreviousResultType == SCREEN_OFF_RESULT) {
- logScreenOff();
- }
- mSensorManager.registerListener(
- this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
+ mActive = shouldBeActive;
+ if (DEBUG) Slog.d(TAG, "Update active - " + shouldBeActive);
}
}
@@ -287,8 +291,10 @@ public class FaceDownDetector implements SensorEventListener {
* The user interacted with the screen while face down, indicated the phone is in use.
* We log this event and temporarily make this detector inactive.
*/
- public void userActivity() {
- mHandler.post(mUserActivityRunnable);
+ public void userActivity(int event) {
+ if (event != PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN) {
+ mHandler.post(mUserActivityRunnable);
+ }
}
private void exitFaceDown(int resultType, long millisSinceFlip) {
@@ -389,6 +395,7 @@ public class FaceDownDetector implements SensorEventListener {
case KEY_TIME_THRESHOLD_MILLIS:
case KEY_FEATURE_ENABLED:
readValuesFromDeviceConfig();
+ updateActiveState();
return;
default:
Slog.i(TAG, "Ignoring change on " + key);
@@ -401,8 +408,18 @@ public class FaceDownDetector implements SensorEventListener {
mZAccelerationThreshold = getZAccelerationThreshold();
mZAccelerationThresholdLenient = mZAccelerationThreshold + 1.0f;
mTimeThreshold = getTimeThreshold();
- mIsEnabled = isEnabled();
mUserInteractionBackoffMillis = getUserInteractionBackoffMillis();
+ final boolean oldEnabled = mIsEnabled;
+ mIsEnabled = isEnabled();
+ if (oldEnabled != mIsEnabled) {
+ if (!mIsEnabled) {
+ mContext.unregisterReceiver(mScreenReceiver);
+ mInteractive = false;
+ } else {
+ registerScreenReceiver(mContext);
+ mInteractive = mContext.getSystemService(PowerManager.class).isInteractive();
+ }
+ }
Slog.i(TAG, "readValuesFromDeviceConfig():"
+ "\nmAccelerationThreshold=" + mAccelerationThreshold
@@ -423,9 +440,11 @@ public class FaceDownDetector implements SensorEventListener {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
- checkAndUpdateActiveState(false);
+ mInteractive = false;
+ updateActiveState();
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
- checkAndUpdateActiveState(true);
+ mInteractive = true;
+ updateActiveState();
}
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index f49e2f1631b9..7555a7f2920b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -549,6 +549,7 @@ public class Notifier {
if (!mUserActivityPending) {
mUserActivityPending = true;
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
+ msg.arg1 = event;
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
@@ -647,7 +648,7 @@ public class Notifier {
mSuspendBlocker.release();
}
- private void sendUserActivity() {
+ private void sendUserActivity(int event) {
synchronized (mLock) {
if (!mUserActivityPending) {
return;
@@ -657,7 +658,7 @@ public class Notifier {
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
tm.notifyUserActivity();
mPolicy.userActivity();
- mFaceDownDetector.userActivity();
+ mFaceDownDetector.userActivity(event);
}
void postEnhancedDischargePredictionBroadcast(long delayMs) {
@@ -833,7 +834,7 @@ public class Notifier {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY:
- sendUserActivity();
+ sendUserActivity(msg.arg1);
break;
case MSG_BROADCAST:
sendNextBroadcast();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 29adde37ab3b..d2a4cd604c01 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -180,8 +180,6 @@ public final class PowerManagerService extends SystemService
private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
// Dirty bit: attentive timer may have timed out
private static final int DIRTY_ATTENTIVE = 1 << 14;
- // Dirty bit: phone flipped to face down
- private static final int DIRTY_FACE_DOWN = 1 << 15;
// Dirty bit: display group power state has changed
private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16;
@@ -1069,8 +1067,9 @@ public final class PowerManagerService extends SystemService
final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, -1L);
millisUntilNormalTimeout =
mLastUserActivityTime + screenOffTimeout - mClock.uptimeMillis();
- mDirty |= DIRTY_FACE_DOWN;
- updatePowerStateLocked();
+ userActivityInternal(mClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN, /* flags= */0,
+ Process.SYSTEM_UID);
}
}
if (isFaceDown) {
diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java
index 2e4b054b829c..c9e81ed7a796 100644
--- a/services/core/java/com/android/server/power/PreRebootLogger.java
+++ b/services/core/java/com/android/server/power/PreRebootLogger.java
@@ -19,7 +19,6 @@ package com.android.server.power;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.content.Context;
-import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -147,7 +146,6 @@ final class PreRebootLogger {
return;
}
- final long token = Binder.clearCallingIdentity();
try {
final File dumpFile = new File(dumpDir, serviceName);
final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
@@ -156,8 +154,6 @@ final class PreRebootLogger {
binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
} catch (FileNotFoundException | RemoteException e) {
Slog.e(TAG, String.format("Failed to dump %s service before reboot", serviceName), e);
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 3a08ddc6c405..fc62f5be801c 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -24,6 +24,7 @@ import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
import android.content.rollback.RollbackInfo;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Os;
@@ -227,6 +228,15 @@ class RollbackStore {
packageSessionIds, extensionVersions);
}
+ private static boolean isLinkPossible(File oldFile, File newFile) {
+ try {
+ return Os.stat(oldFile.getAbsolutePath()).st_dev
+ == Os.stat(newFile.getAbsolutePath()).st_dev;
+ } catch (ErrnoException ignore) {
+ return false;
+ }
+ }
+
/**
* Creates a backup copy of an apk or apex for a package.
* For packages containing splits, this method should be called for each
@@ -239,16 +249,29 @@ class RollbackStore {
targetDir.mkdirs();
File targetFile = new File(targetDir, sourceFile.getName());
- try {
- // Create a hard link to avoid copy
- // TODO(b/168562373)
- // Linking between non-encrypted and encrypted is not supported and we have
- // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works
- // because we happen to store encrypted files under /data/apex/active which is no
- // longer the case when compressed apex rolls out. We have to handle this case in
- // order not to fall back to copy.
- Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
- } catch (ErrnoException ignore) {
+ boolean fallbackToCopy = !isLinkPossible(sourceFile, targetFile);
+ if (!fallbackToCopy) {
+ try {
+ // Create a hard link to avoid copy
+ // TODO(b/168562373)
+ // Linking between non-encrypted and encrypted is not supported and we have
+ // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works
+ // because we happen to store encrypted files under /data/apex/active which is no
+ // longer the case when compressed apex rolls out. We have to handle this case in
+ // order not to fall back to copy.
+ Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
+ } catch (ErrnoException e) {
+ boolean isRollbackTest =
+ SystemProperties.getBoolean("persist.rollback.is_test", false);
+ if (isRollbackTest) {
+ throw new IOException(e);
+ } else {
+ fallbackToCopy = true;
+ }
+ }
+ }
+
+ if (fallbackToCopy) {
// Fall back to copy if hardlink can't be created
Files.copy(sourceFile.toPath(), targetFile.toPath());
}
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index cfb4c27820fa..189f47f7f1da 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -77,9 +77,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
@GuardedBy("mLock")
public void resolveRotationLocked(RotationRequest request) {
- final RotationResolutionRequest remoteRequest = new RotationResolutionRequest(
- request.mProposedRotation, request.mCurrentRotation, request.mPackageName,
- request.mTimeoutMillis);
+ final RotationResolutionRequest remoteRequest = request.mRemoteRequest;
post(service -> service.resolveRotation(request.mIRotationResolverCallback, remoteRequest));
// schedule a timeout.
@@ -91,7 +89,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
request.cancelInternal();
}
}
- }, request.mTimeoutMillis);
+ }, request.mRemoteRequest.getTimeoutMillis());
}
@VisibleForTesting
@@ -109,28 +107,18 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
@GuardedBy("mLock")
boolean mIsFulfilled;
- private final long mTimeoutMillis;
-
@VisibleForTesting
- final int mProposedRotation;
-
- private final int mCurrentRotation;
- private final String mPackageName;
+ final RotationResolutionRequest mRemoteRequest;
boolean mIsDispatched;
private final Object mLock = new Object();
private final long mRequestStartTimeMillis;
RotationRequest(
- @NonNull RotationResolverInternal.RotationResolverCallbackInternal
- callbackInternal, int proposedRotation, int currentRotation,
- String packageName, long timeoutMillis,
- @NonNull CancellationSignal cancellationSignal) {
- mTimeoutMillis = timeoutMillis;
+ @NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
+ RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal) {
mCallbackInternal = callbackInternal;
- mProposedRotation = proposedRotation;
- mCurrentRotation = currentRotation;
- mPackageName = packageName;
+ mRemoteRequest = request;
mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
@@ -185,8 +173,8 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
request.mCallbackInternal.onSuccess(rotation);
final long timeToCalculate =
SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
- logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation,
- timeToCalculate);
+ logRotationStats(request.mRemoteRequest.getProposedRotation(),
+ request.mRemoteRequest.getCurrentRotation(), rotation, timeToCalculate);
Slog.d(TAG, "onSuccess:" + rotation);
Slog.d(TAG, "timeToCalculate:" + timeToCalculate);
}
@@ -204,8 +192,9 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
request.mCallbackInternal.onFailure(error);
final long timeToCalculate =
SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
- logRotationStats(request.mProposedRotation, request.mCurrentRotation,
- RESOLUTION_FAILURE, timeToCalculate);
+ logRotationStats(request.mRemoteRequest.getProposedRotation(),
+ request.mRemoteRequest.getCurrentRotation(), RESOLUTION_FAILURE,
+ timeToCalculate);
Slog.d(TAG, "onFailure:" + error);
Slog.d(TAG, "timeToCalculate:" + timeToCalculate);
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 13f8d61f74f5..1dbe3e485938 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -34,11 +34,11 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.CancellationSignal;
import android.rotationresolver.RotationResolverInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
import android.service.rotationresolver.RotationResolverService;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
-import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,14 +95,14 @@ final class RotationResolverManagerPerUserService extends
@VisibleForTesting
void resolveRotationLocked(
@NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
- @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
- String packageName, long timeoutMillis,
+ @NonNull RotationResolutionRequest request,
@NonNull CancellationSignal cancellationSignalInternal) {
if (!isServiceAvailableLocked()) {
Slog.w(TAG, "Service is not available at this moment.");
callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
- logRotationStats(proposedRotation, currentRotation, RESOLUTION_UNAVAILABLE);
+ logRotationStats(request.getProposedRotation(), request.getCurrentRotation(),
+ RESOLUTION_UNAVAILABLE);
return;
}
@@ -114,8 +114,7 @@ final class RotationResolverManagerPerUserService extends
}
mCurrentRequest = new RemoteRotationResolverService.RotationRequest(callbackInternal,
- proposedRotation, currentRotation, packageName, timeoutMillis,
- cancellationSignalInternal);
+ request, cancellationSignalInternal);
cancellationSignalInternal.setOnCancelListener(() -> {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index e57d4ce9ec31..a7f3cdb8bcd2 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -35,6 +35,7 @@ import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.rotationresolver.RotationResolverInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -158,8 +159,9 @@ public class RotationResolverManagerService extends
if (mIsServiceEnabled) {
final RotationResolverManagerPerUserService service = getServiceForUserLocked(
UserHandle.getCallingUserId());
- service.resolveRotationLocked(callbackInternal, proposedRotation,
- currentRotation, /* packageName */ "", timeout,
+ final RotationResolutionRequest request = new RotationResolutionRequest("",
+ currentRotation, proposedRotation, true, timeout);
+ service.resolveRotationLocked(callbackInternal, request,
cancellationSignalInternal);
} else {
Slog.w(TAG, "Rotation Resolver service is disabled.");
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index e5088c023533..a0e04ee7a20d 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.os.CancellationSignal;
import android.os.ShellCommand;
import android.rotationresolver.RotationResolverInternal.RotationResolverCallbackInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
import android.text.TextUtils;
import android.view.Surface;
@@ -107,8 +108,10 @@ final class RotationResolverShellCommand extends ShellCommand {
}
private int runResolveRotation() {
- mService.resolveRotationLocked(sTestableRotationCallbackInternal, Surface.ROTATION_0,
- Surface.ROTATION_0, "", 2000L, new CancellationSignal());
+ final RotationResolutionRequest request = new RotationResolutionRequest("",
+ Surface.ROTATION_0, Surface.ROTATION_0, true, 2000L);
+ mService.resolveRotationLocked(sTestableRotationCallbackInternal, request,
+ new CancellationSignal());
return 0;
}
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 74bb99351a6d..466ac74a8322 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -29,6 +29,7 @@ import android.os.UserHandle;
import android.security.IFileIntegrityService;
import android.util.Slog;
+import com.android.internal.security.VerityUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
index 91b240bcb189..e6f5826557b5 100644
--- a/services/core/java/com/android/server/security/OWNERS
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 36824
per-file FileIntegrityService.java = victorhsieh@google.com
-per-file VerityUtils.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
index dbe73546d748..52c1467bd5d0 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.speech;
+import static android.Manifest.permission.MANAGE_SPEECH_RECOGNITION;
+
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ComponentName;
@@ -24,6 +26,7 @@ import android.os.IBinder;
import android.os.UserHandle;
import android.speech.IRecognitionServiceManager;
import android.speech.IRecognitionServiceManagerCallback;
+import android.util.Slog;
import com.android.internal.R;
import com.android.server.infra.AbstractMasterSystemService;
@@ -42,6 +45,8 @@ public final class SpeechRecognitionManagerService extends
SpeechRecognitionManagerServiceImpl> {
private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName();
+ private static final int MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS = 60_000;
+
public SpeechRecognitionManagerService(@NonNull Context context) {
super(context,
// TODO(b/176578753): think if we want to favor the particular service here.
@@ -58,6 +63,16 @@ public final class SpeechRecognitionManagerService extends
}
@Override
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_SPEECH_RECOGNITION, TAG);
+ }
+
+ @Override
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS;
+ }
+
+ @Override
protected SpeechRecognitionManagerServiceImpl newServiceLocked(
@UserIdInt int resolvedUserId, boolean disabled) {
return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
@@ -77,5 +92,21 @@ public final class SpeechRecognitionManagerService extends
service.createSessionLocked(componentName, clientToken, onDevice, callback);
}
}
+
+ @Override
+ public void setTemporaryComponent(ComponentName componentName) {
+ int userId = UserHandle.getCallingUserId();
+ if (componentName == null) {
+ resetTemporaryService(userId);
+ Slog.i(TAG, "Reset temporary service for user " + userId);
+ return;
+ }
+ setTemporaryService(
+ userId,
+ componentName.flattenToString(),
+ MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS);
+ Slog.i(TAG, "SpeechRecognition temporarily set to " + componentName + " for "
+ + MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS + "ms");
+ }
}
}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 2656a3d32555..769e049c8d0e 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -100,6 +100,9 @@ final class SpeechRecognitionManagerServiceImpl extends
}
if (serviceComponent == null) {
+ if (mMaster.debug) {
+ Slog.i(TAG, "Service component is undefined, responding with error.");
+ }
tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT);
return;
}
@@ -213,6 +216,10 @@ final class SpeechRecognitionManagerServiceImpl extends
@Nullable
private ComponentName getOnDeviceComponentNameLocked() {
final String serviceName = getComponentNameLocked();
+ if (mMaster.debug) {
+ Slog.i(TAG, "Resolved component name: " + serviceName);
+ }
+
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
@@ -241,6 +248,11 @@ final class SpeechRecognitionManagerServiceImpl extends
service.getServiceComponentName().equals(serviceComponent))
.findFirst();
if (existingService.isPresent()) {
+
+ if (mMaster.debug) {
+ Slog.i(TAG, "Reused existing connection to " + serviceComponent);
+ }
+
return existingService.get();
}
}
@@ -253,6 +265,10 @@ final class SpeechRecognitionManagerServiceImpl extends
mRemoteServicesByUid.computeIfAbsent(callingUid, key -> new HashSet<>());
valuesByCaller.add(service);
+ if (mMaster.debug) {
+ Slog.i(TAG, "Creating a new connection to " + serviceComponent);
+ }
+
return service;
}
}
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index fc7fd220b26a..174ad3ad2e25 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,7 +1,10 @@
jeffreyhuang@google.com
joeo@google.com
+jtnguyen@google.com
muhammadq@google.com
+rslawik@google.com
ruchirr@google.com
+sharaienko@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7ed7a592a972..8023fd42edfa 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -148,11 +148,13 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+import com.android.internal.os.KernelSingleProcessCpuThreadReader.ProcessCpuUsage;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.SelectedProcessCpuThreadReader;
import com.android.internal.os.StoragedUidIoStatsReader;
import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.internal.util.CollectionUtils;
@@ -351,6 +353,8 @@ public class StatsPullAtomService extends SystemService {
@GuardedBy("mDataBytesTransferLock")
private final ArrayList<SubInfo> mHistoricalSubs = new ArrayList<>();
+ private SelectedProcessCpuThreadReader mSurfaceFlingerProcessCpuThreadReader;
+
// Puller locks
private final Object mDataBytesTransferLock = new Object();
private final Object mBluetoothBytesTransferLock = new Object();
@@ -753,6 +757,9 @@ public class StatsPullAtomService extends SystemService {
}
}
}
+
+ mSurfaceFlingerProcessCpuThreadReader =
+ new SelectedProcessCpuThreadReader("/system/bin/surfaceflinger");
}
void registerEventListeners() {
@@ -1479,7 +1486,7 @@ public class StatsPullAtomService extends SystemService {
}
for (int freqIndex = 0; freqIndex < timesMs.length; ++freqIndex) {
int cluster = freqsClusters[freqIndex];
- long freq = freqs[freqIndex];
+ int freq = (int) freqs[freqIndex];
long timeMs = timesMs[freqIndex];
pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs));
}
@@ -1678,6 +1685,18 @@ public class StatsPullAtomService extends SystemService {
FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER,
times.binderThreadCpuTimesUs);
+ ProcessCpuUsage surfaceFlingerTimes = mSurfaceFlingerProcessCpuThreadReader.readAbsolute();
+ if (surfaceFlingerTimes != null && surfaceFlingerTimes.threadCpuTimesMillis != null) {
+ long[] surfaceFlingerTimesUs =
+ new long[surfaceFlingerTimes.threadCpuTimesMillis.length];
+ for (int i = 0; i < surfaceFlingerTimesUs.length; ++i) {
+ surfaceFlingerTimesUs[i] = surfaceFlingerTimes.threadCpuTimesMillis[i] * 1_000;
+ }
+ addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
+ FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SURFACE_FLINGER,
+ surfaceFlingerTimesUs);
+ }
+
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
index 628c1d61ce9a..9f8b27f5f1bf 100644
--- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
@@ -26,7 +26,7 @@ final class SystemMemoryUtil {
private SystemMemoryUtil() {}
static Metrics getMetrics() {
- int totalIonKb = (int) Debug.getIonHeapsSizeKb();
+ int totalIonKb = (int) Debug.getDmabufHeapTotalExportedKb();
int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb();
int gpuDmaBufUsageKb = (int) Debug.getGpuDmaBufUsageKb();
int dmaBufTotalExportedKb = (int) Debug.getDmabufTotalExportedKb();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 7523671fb3a7..970420a284d6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -160,4 +160,10 @@ public interface StatusBarManagerInternal {
* Handles a logging command from the WM shell command.
*/
void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd);
+
+ /**
+ * @see com.android.internal.statusbar.IStatusBar#setNavigationBarLumaSamplingEnabled(int,
+ * boolean)
+ */
+ void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 546e420c1d59..302a23fb262c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -588,6 +588,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
} catch (RemoteException ex) { }
}
}
+
+ @Override
+ public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ if (mBar != null) {
+ try {
+ mBar.setNavigationBarLumaSamplingEnabled(displayId, enable);
+ } catch (RemoteException ex) { }
+ }
+ }
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
new file mode 100644
index 000000000000..f00f856c2910
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timedetector;
+
+import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
+
+import android.annotation.UserIdInt;
+import android.app.time.Capabilities.CapabilityState;
+import android.app.time.TimeCapabilities;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
+import android.os.UserHandle;
+
+import java.util.Objects;
+
+/**
+ * Holds configuration values that affect time behaviour.
+ */
+public final class ConfigurationInternal {
+
+ private final @UserIdInt int mUserId;
+ private final boolean mUserConfigAllowed;
+ private final boolean mAutoDetectionEnabled;
+
+ private ConfigurationInternal(Builder builder) {
+ mUserId = builder.mUserId;
+ mUserConfigAllowed = builder.mUserConfigAllowed;
+ mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
+ }
+
+ /** Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values. */
+ public TimeCapabilitiesAndConfig capabilitiesAndConfig() {
+ return new TimeCapabilitiesAndConfig(timeCapabilities(), timeConfiguration());
+ }
+
+ private TimeConfiguration timeConfiguration() {
+ return new TimeConfiguration.Builder()
+ .setAutoDetectionEnabled(mAutoDetectionEnabled)
+ .build();
+ }
+
+ private TimeCapabilities timeCapabilities() {
+ @CapabilityState int configureAutoTimeDetectionEnabledCapability =
+ mUserConfigAllowed
+ ? CAPABILITY_POSSESSED
+ : CAPABILITY_NOT_ALLOWED;
+
+ @CapabilityState int suggestTimeManuallyCapability =
+ mUserConfigAllowed
+ ? CAPABILITY_POSSESSED
+ : CAPABILITY_NOT_ALLOWED;
+
+ return new TimeCapabilities.Builder(UserHandle.of(mUserId))
+ .setConfigureAutoTimeDetectionEnabledCapability(
+ configureAutoTimeDetectionEnabledCapability)
+ .setSuggestTimeManuallyCapability(suggestTimeManuallyCapability)
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ConfigurationInternal that = (ConfigurationInternal) o;
+ return mUserId == that.mUserId
+ && mUserConfigAllowed == that.mUserConfigAllowed
+ && mAutoDetectionEnabled == that.mAutoDetectionEnabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionEnabled);
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigurationInternal{"
+ + "mUserId=" + mUserId
+ + ", mUserConfigAllowed=" + mUserConfigAllowed
+ + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
+ + '}';
+ }
+
+ static final class Builder {
+ private final @UserIdInt int mUserId;
+ private boolean mUserConfigAllowed;
+ private boolean mAutoDetectionEnabled;
+
+ Builder(@UserIdInt int userId) {
+ mUserId = userId;
+ }
+
+ Builder setUserConfigAllowed(boolean userConfigAllowed) {
+ mUserConfigAllowed = userConfigAllowed;
+ return this;
+ }
+
+ Builder setAutoDetectionEnabled(boolean autoDetectionEnabled) {
+ mAutoDetectionEnabled = autoDetectionEnabled;
+ return this;
+ }
+
+ ConfigurationInternal build() {
+ return new ConfigurationInternal(this);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
index 5cd171839996..4f5e8fa9c944 100644
--- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
@@ -21,6 +21,7 @@ import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPH
import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -28,6 +29,8 @@ import android.os.Build;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;
@@ -71,6 +74,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
@NonNull private final ContentResolver mContentResolver;
@NonNull private final PowerManager.WakeLock mWakeLock;
@NonNull private final AlarmManager mAlarmManager;
+ @NonNull private final UserManager mUserManager;
@NonNull private final int[] mOriginPriorities;
public EnvironmentImpl(@NonNull Context context) {
@@ -83,6 +87,8 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class));
+ mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class));
+
mSystemClockUpdateThresholdMillis =
SystemProperties.getInt("ro.sys.time_detector_update_diff",
SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
@@ -115,6 +121,14 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
}
@Override
+ public ConfigurationInternal configurationInternal(@UserIdInt int userId) {
+ return new ConfigurationInternal.Builder(userId)
+ .setUserConfigAllowed(isUserConfigAllowed(userId))
+ .setAutoDetectionEnabled(isAutoTimeDetectionEnabled())
+ .build();
+ }
+
+ @Override
public void acquireWakeLock() {
if (mWakeLock.isHeld()) {
Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held");
@@ -150,6 +164,11 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
}
}
+ private boolean isUserConfigAllowed(@UserIdInt int userId) {
+ UserHandle userHandle = UserHandle.of(userId);
+ return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
+ }
+
private static int[] getOriginPriorities(@NonNull Context context) {
String[] originStrings =
context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index b210339adf79..eefa045abe1b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -18,7 +18,10 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.time.ExternalTimeSuggestion;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
@@ -36,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
+import com.android.server.timezonedetector.CallerIdentityInjector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,6 +75,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
@NonNull private final Handler mHandler;
@NonNull private final Context mContext;
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
+ @NonNull private final CallerIdentityInjector mCallerIdentityInjector;
private static TimeDetectorService create(@NonNull Context context) {
TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context);
@@ -97,9 +102,42 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
@VisibleForTesting
public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
+ this(context, handler, timeDetectorStrategy, CallerIdentityInjector.REAL);
+ }
+
+ @VisibleForTesting
+ public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
+ @NonNull TimeDetectorStrategy timeDetectorStrategy,
+ @NonNull CallerIdentityInjector callerIdentityInjector) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
+ mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector);
+ }
+
+ @Override
+ public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() {
+ int userId = mCallerIdentityInjector.getCallingUserId();
+ return getTimeCapabilitiesAndConfig(userId);
+ }
+
+ private TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig(@UserIdInt int userId) {
+ enforceManageTimeDetectorPermission();
+
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
+ try {
+ ConfigurationInternal configurationInternal =
+ mTimeDetectorStrategy.getConfigurationInternal(userId);
+ return configurationInternal.capabilitiesAndConfig();
+ } finally {
+ mCallerIdentityInjector.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean updateConfiguration(TimeConfiguration timeConfiguration) {
+ // TODO(b/172891783) Add actual logic
+ return false;
}
@Override
@@ -193,4 +231,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
android.Manifest.permission.SET_TIME,
"suggest time from external source");
}
+
+ private void enforceManageTimeDetectorPermission() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
+ "manage time and time zone detection");
+ }
+
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 792f372b0c49..cde66becdee2 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -18,6 +18,7 @@ package com.android.server.timedetector;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
@@ -88,6 +89,9 @@ public interface TimeDetectorStrategy extends Dumpable {
/** Processes the suggested time from external sources. */
void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion);
+ /** Returns the configuration that controls time detector behaviour for specified user. */
+ ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
+
/**
* Handles the auto-time configuration changing For example, when the auto-time setting is
* toggled on or off.
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 16e8632c6e40..289d8d6e648e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -22,6 +22,7 @@ import static java.util.stream.Collectors.joining;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
@@ -155,6 +156,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
*/
@Origin int[] autoOriginPriorities();
+ /**
+ * Returns {@link ConfigurationInternal} for specified user.
+ */
+ @NonNull
+ ConfigurationInternal configurationInternal(@UserIdInt int userId);
+
/** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
void acquireWakeLock();
@@ -267,6 +274,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
}
@Override
+ @NonNull
+ public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
+ return mEnvironment.configurationInternal(userId);
+ }
+
+ @Override
public synchronized void handleAutoTimeConfigChanged() {
boolean enabled = mEnvironment.isAutoTimeDetectionEnabled();
// When automatic time detection is enabled we update the system clock instantly if we can.
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 3ae9d641e81c..b4aa20130791 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -16,10 +16,10 @@
package com.android.server.timezonedetector;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -28,8 +28,6 @@ import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import android.os.UserHandle;
-import com.android.internal.util.Preconditions;
-
import java.util.Objects;
/**
@@ -39,7 +37,7 @@ import java.util.Objects;
*/
public final class ConfigurationInternal {
- private final boolean mAutoDetectionSupported;
+ private final boolean mTelephonyDetectionSupported;
private final boolean mGeoDetectionSupported;
private final boolean mAutoDetectionEnabled;
private final @UserIdInt int mUserId;
@@ -48,7 +46,7 @@ public final class ConfigurationInternal {
private final boolean mGeoDetectionEnabled;
private ConfigurationInternal(Builder builder) {
- mAutoDetectionSupported = builder.mAutoDetectionSupported;
+ mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
mGeoDetectionSupported = builder.mGeoDetectionSupported;
mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
@@ -56,14 +54,16 @@ public final class ConfigurationInternal {
mUserConfigAllowed = builder.mUserConfigAllowed;
mLocationEnabled = builder.mLocationEnabled;
mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
- // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
- // cannot be true if mAutoDetectionSupported == false
- Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
}
/** Returns true if the device supports any form of auto time zone detection. */
public boolean isAutoDetectionSupported() {
- return mAutoDetectionSupported;
+ return mTelephonyDetectionSupported || mGeoDetectionSupported;
+ }
+
+ /** Returns true if the device supports telephony time zone detection. */
+ public boolean isTelephonyDetectionSupported() {
+ return mTelephonyDetectionSupported;
}
/** Returns true if the device supports geolocation time zone detection. */
@@ -78,9 +78,10 @@ public final class ConfigurationInternal {
/**
* Returns true if auto time zone detection behavior is actually enabled, which can be distinct
- * from the raw setting value. */
+ * from the raw setting value.
+ */
public boolean getAutoDetectionEnabledBehavior() {
- return mAutoDetectionSupported && mAutoDetectionEnabled;
+ return isAutoDetectionSupported() && mAutoDetectionEnabled;
}
/** Returns the ID of the user this configuration is associated with. */
@@ -212,7 +213,7 @@ public final class ConfigurationInternal {
ConfigurationInternal that = (ConfigurationInternal) o;
return mUserId == that.mUserId
&& mUserConfigAllowed == that.mUserConfigAllowed
- && mAutoDetectionSupported == that.mAutoDetectionSupported
+ && mTelephonyDetectionSupported == that.mTelephonyDetectionSupported
&& mGeoDetectionSupported == that.mGeoDetectionSupported
&& mAutoDetectionEnabled == that.mAutoDetectionEnabled
&& mLocationEnabled == that.mLocationEnabled
@@ -221,7 +222,7 @@ public final class ConfigurationInternal {
@Override
public int hashCode() {
- return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
+ return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled,
mGeoDetectionEnabled);
}
@@ -231,7 +232,7 @@ public final class ConfigurationInternal {
return "ConfigurationInternal{"
+ "mUserId=" + mUserId
+ ", mUserConfigAllowed=" + mUserConfigAllowed
- + ", mAutoDetectionSupported=" + mAutoDetectionSupported
+ + ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
+ ", mGeoDetectionSupported=" + mGeoDetectionSupported
+ ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
+ ", mLocationEnabled=" + mLocationEnabled
@@ -247,7 +248,7 @@ public final class ConfigurationInternal {
private final @UserIdInt int mUserId;
private boolean mUserConfigAllowed;
- private boolean mAutoDetectionSupported;
+ private boolean mTelephonyDetectionSupported;
private boolean mGeoDetectionSupported;
private boolean mAutoDetectionEnabled;
private boolean mLocationEnabled;
@@ -266,7 +267,7 @@ public final class ConfigurationInternal {
public Builder(ConfigurationInternal toCopy) {
this.mUserId = toCopy.mUserId;
this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
- this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+ this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported;
this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
this.mLocationEnabled = toCopy.mLocationEnabled;
@@ -282,10 +283,10 @@ public final class ConfigurationInternal {
}
/**
- * Sets whether any form of automatic time zone detection is supported on this device.
+ * Sets whether telephony time zone detection is supported on this device.
*/
- public Builder setAutoDetectionFeatureSupported(boolean supported) {
- mAutoDetectionSupported = supported;
+ public Builder setTelephonyDetectionFeatureSupported(boolean supported) {
+ mTelephonyDetectionSupported = supported;
return this;
}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index e3caae9482d9..0e5f3bfbb4b1 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -128,8 +128,8 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
@Override
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
return new ConfigurationInternal.Builder(userId)
- .setAutoDetectionFeatureSupported(
- mServiceConfigAccessor.isAutoDetectionFeatureSupported())
+ .setTelephonyDetectionFeatureSupported(
+ mServiceConfigAccessor.isTelephonyTimeZoneDetectionFeatureSupported())
.setGeoDetectionFeatureSupported(
mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported())
.setAutoDetectionEnabled(isAutoDetectionEnabled())
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
new file mode 100644
index 000000000000..c8c828f10ad3
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that provides time zone detector state information for metrics.
+ *
+ * <p>
+ * Regarding time zone ID ordinals:
+ * <p>
+ * We don't want to leak user location information by reporting time zone IDs. Instead, time zone
+ * IDs are consistently identified within a given instance of this class by a numeric ID. This
+ * allows comparison of IDs without revealing what those IDs are.
+ */
+public final class MetricsTimeZoneDetectorState {
+
+ @IntDef(prefix = "DETECTION_MODE_",
+ value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY})
+ @interface DetectionMode {};
+
+ @DetectionMode
+ public static final int DETECTION_MODE_MANUAL = 0;
+ @DetectionMode
+ public static final int DETECTION_MODE_GEO = 1;
+ @DetectionMode
+ public static final int DETECTION_MODE_TELEPHONY = 2;
+
+ @NonNull
+ private final ConfigurationInternal mConfigurationInternal;
+ @NonNull
+ private final int mDeviceTimeZoneIdOrdinal;
+ @Nullable
+ private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
+ @Nullable
+ private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
+ @Nullable
+ private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
+
+ private MetricsTimeZoneDetectorState(
+ @NonNull ConfigurationInternal configurationInternal,
+ int deviceTimeZoneIdOrdinal,
+ @Nullable MetricsTimeZoneSuggestion latestManualSuggestion,
+ @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion,
+ @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) {
+ mConfigurationInternal = Objects.requireNonNull(configurationInternal);
+ mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal;
+ mLatestManualSuggestion = latestManualSuggestion;
+ mLatestTelephonySuggestion = latestTelephonySuggestion;
+ mLatestGeolocationSuggestion = latestGeolocationSuggestion;
+ }
+
+ /**
+ * Creates {@link MetricsTimeZoneDetectorState} from the supplied parameters, using the {@link
+ * OrdinalGenerator} to generate time zone ID ordinals.
+ */
+ public static MetricsTimeZoneDetectorState create(
+ @NonNull OrdinalGenerator<String> tzIdOrdinalGenerator,
+ @NonNull ConfigurationInternal configurationInternal,
+ @NonNull String deviceTimeZoneId,
+ @Nullable ManualTimeZoneSuggestion latestManualSuggestion,
+ @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
+ @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
+
+ // TODO(b/172934905) Add logic to canonicalize the time zone IDs to Android's preferred IDs
+ // so that the ordinals will match even when the ID is not identical, just equivalent.
+ int deviceTimeZoneIdOrdinal =
+ tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
+ MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion =
+ createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion);
+ MetricsTimeZoneSuggestion latestObfuscatedTelephonySuggestion =
+ createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion);
+ MetricsTimeZoneSuggestion latestObfuscatedGeolocationSuggestion =
+ createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion);
+
+ return new MetricsTimeZoneDetectorState(
+ configurationInternal, deviceTimeZoneIdOrdinal, latestObfuscatedManualSuggestion,
+ latestObfuscatedTelephonySuggestion, latestObfuscatedGeolocationSuggestion);
+ }
+
+ /** Returns true if the device supports telephony time zone detection. */
+ public boolean isTelephonyDetectionSupported() {
+ return mConfigurationInternal.isTelephonyDetectionSupported();
+ }
+
+ /** Returns true if the device supports geolocation time zone detection. */
+ public boolean isGeoDetectionSupported() {
+ return mConfigurationInternal.isGeoDetectionSupported();
+ }
+
+ /** Returns true if user's location can be used generally. */
+ public boolean isUserLocationEnabled() {
+ return mConfigurationInternal.isLocationEnabled();
+ }
+
+ /** Returns the value of the geolocation time zone detection enabled setting. */
+ public boolean getGeoDetectionEnabledSetting() {
+ return mConfigurationInternal.getGeoDetectionEnabledSetting();
+ }
+
+ /** Returns the value of the auto time zone detection enabled setting. */
+ public boolean getAutoDetectionEnabledSetting() {
+ return mConfigurationInternal.getAutoDetectionEnabledSetting();
+ }
+
+ /**
+ * Returns the detection mode the device is currently using, which can be influenced by various
+ * things besides the user's setting.
+ */
+ @DetectionMode
+ public int getDetectionMode() {
+ if (!mConfigurationInternal.getAutoDetectionEnabledBehavior()) {
+ return DETECTION_MODE_MANUAL;
+ } else if (mConfigurationInternal.getGeoDetectionEnabledBehavior()) {
+ return DETECTION_MODE_GEO;
+ } else {
+ return DETECTION_MODE_TELEPHONY;
+ }
+ }
+
+ /**
+ * Returns the ordinal for the device's currently set time zone ID.
+ * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
+ */
+ @NonNull
+ public int getDeviceTimeZoneIdOrdinal() {
+ return mDeviceTimeZoneIdOrdinal;
+ }
+
+ /**
+ * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last manual
+ * suggestion received.
+ */
+ @Nullable
+ public byte[] getLatestManualSuggestionProtoBytes() {
+ return suggestionProtoBytes(mLatestManualSuggestion);
+ }
+
+ /**
+ * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last, best
+ * telephony suggestion received.
+ */
+ @Nullable
+ public byte[] getLatestTelephonySuggestionProtoBytes() {
+ return suggestionProtoBytes(mLatestTelephonySuggestion);
+ }
+
+ /**
+ * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last geolocation
+ * suggestion received.
+ */
+ @Nullable
+ public byte[] getLatestGeolocationSuggestionProtoBytes() {
+ return suggestionProtoBytes(mLatestGeolocationSuggestion);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o;
+ return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal
+ && mConfigurationInternal.equals(that.mConfigurationInternal)
+ && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion)
+ && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion)
+ && Objects.equals(mLatestGeolocationSuggestion, that.mLatestGeolocationSuggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal,
+ mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsTimeZoneDetectorState{"
+ + "mConfigurationInternal=" + mConfigurationInternal
+ + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+ + ", mLatestManualSuggestion=" + mLatestManualSuggestion
+ + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
+ + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
+ + '}';
+ }
+
+ private static byte[] suggestionProtoBytes(
+ @Nullable MetricsTimeZoneSuggestion suggestion) {
+ if (suggestion == null) {
+ return null;
+ }
+ return suggestion.toBytes();
+ }
+
+ @Nullable
+ private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+ @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+ @NonNull ManualTimeZoneSuggestion manualSuggestion) {
+ if (manualSuggestion == null) {
+ return null;
+ }
+
+ int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId());
+ return MetricsTimeZoneSuggestion.createCertain(
+ new int[] { zoneIdOrdinal });
+ }
+
+ @Nullable
+ private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+ @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+ @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) {
+ if (telephonySuggestion == null) {
+ return null;
+ }
+ if (telephonySuggestion.getZoneId() == null) {
+ return MetricsTimeZoneSuggestion.createUncertain();
+ }
+ int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId());
+ return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal });
+ }
+
+ @Nullable
+ private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+ @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+ @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) {
+ if (geolocationSuggestion == null) {
+ return null;
+ }
+
+ List<String> zoneIds = geolocationSuggestion.getZoneIds();
+ if (zoneIds == null) {
+ return MetricsTimeZoneSuggestion.createUncertain();
+ }
+ return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds));
+ }
+
+ /**
+ * A Java class that closely matches the android.app.time.MetricsTimeZoneSuggestion
+ * proto definition.
+ */
+ private static final class MetricsTimeZoneSuggestion {
+ @Nullable
+ private final int[] mZoneIdOrdinals;
+
+ MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) {
+ mZoneIdOrdinals = zoneIdOrdinals;
+ }
+
+ @NonNull
+ static MetricsTimeZoneSuggestion createUncertain() {
+ return new MetricsTimeZoneSuggestion(null);
+ }
+
+ public static MetricsTimeZoneSuggestion createCertain(
+ @NonNull int[] zoneIdOrdinals) {
+ return new MetricsTimeZoneSuggestion(zoneIdOrdinals);
+ }
+
+ boolean isCertain() {
+ return mZoneIdOrdinals != null;
+ }
+
+ @Nullable
+ int[] getZoneIdOrdinals() {
+ return mZoneIdOrdinals;
+ }
+
+ byte[] toBytes() {
+ // We don't get access to the atoms.proto definition for nested proto fields, so we use
+ // an identically specified proto.
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream);
+ int typeProtoValue = isCertain()
+ ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN
+ : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN;
+ protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE,
+ typeProtoValue);
+ if (isCertain()) {
+ for (int zoneIdOrdinal : getZoneIdOrdinals()) {
+ protoOutputStream.write(
+ android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
+ zoneIdOrdinal);
+ }
+ }
+ protoOutputStream.flush();
+ closeQuietly(byteArrayOutputStream);
+ return byteArrayOutputStream.toByteArray();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o;
+ return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mZoneIdOrdinals);
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsTimeZoneSuggestion{"
+ + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals)
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
new file mode 100644
index 000000000000..a448773c40d5
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import android.util.ArraySet;
+
+import java.util.List;
+
+/**
+ * A helper class that turns a set of objects into ordinal values, i.e. each object is offered
+ * up via {@link #ordinal(Object)} or similar method, and a number will be returned. If the
+ * object has been seen before by the instance then the same number will be returned. Intended
+ * for situations where it is useful to know if values from some finite set are the same or
+ * different, but the value is either large or may reveal PII. This class relies on {@link
+ * Object#equals(Object)} and {@link Object#hashCode()}.
+ */
+class OrdinalGenerator<T> {
+ private final ArraySet<T> mKnownIds = new ArraySet<>();
+
+ int ordinal(T object) {
+ int ordinal = mKnownIds.indexOf(object);
+ if (ordinal < 0) {
+ ordinal = mKnownIds.size();
+ mKnownIds.add(object);
+ }
+ return ordinal;
+ }
+
+ int[] ordinals(List<T> objects) {
+ int[] ordinals = new int[objects.size()];
+ for (int i = 0; i < ordinals.length; i++) {
+ ordinals[i] = ordinal(objects.get(i));
+ }
+ return ordinals;
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 86c32f8d7b45..2452c8d3ddc9 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -115,10 +115,15 @@ public final class ServiceConfigAccessor {
/** Returns {@code true} if any form of automatic time zone detection is supported. */
public boolean isAutoDetectionFeatureSupported() {
- return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported();
+ return isTelephonyTimeZoneDetectionFeatureSupported()
+ || isGeoTimeZoneDetectionFeatureSupported();
}
- private boolean deviceHasTelephonyNetwork() {
+ /**
+ * Returns {@code true} if the telephony-based time zone detection feature is supported on the
+ * device.
+ */
+ public boolean isTelephonyTimeZoneDetectionFeatureSupported() {
// TODO b/150583524 Avoid the use of a deprecated API.
return mContext.getSystemService(ConnectivityManager.class)
.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index cd220b164851..d429b8762a7c 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -50,4 +50,8 @@ public interface TimeZoneDetectorInternal extends Dumpable.Container {
* available, and so on. This method may be implemented asynchronously.
*/
void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
+
+ /** Generates a state snapshot for metrics. */
+ @NonNull
+ MetricsTimeZoneDetectorState generateMetricsState();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 2d5dacdd6acc..4e78f5aa444c 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -86,8 +86,14 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter
@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
Objects.requireNonNull(timeZoneSuggestion);
- // All strategy calls must take place on the mHandler thread.
+ // This call can take place on the mHandler thread because there is no return value.
mHandler.post(
() -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
}
+
+ @Override
+ @NonNull
+ public MetricsTimeZoneDetectorState generateMetricsState() {
+ return mTimeZoneDetectorStrategy.generateMetricsState();
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 8266f121822e..e3f31b6aa326 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -66,9 +66,10 @@ import android.util.IndentingPrintWriter;
* <p>Threading:
*
* <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
- * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug
- * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread
- * concurrently with other operations.
+ * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()},
+ * {@link #generateMetricsState()} and debug calls like {@link
+ * #dump(IndentingPrintWriter, String[])}, may be called on a different thread concurrently with
+ * other operations.
*
* @hide
*/
@@ -123,4 +124,8 @@ public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
* suggestion.
*/
void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
+
+ /** Generates a state snapshot for metrics. */
+ @NonNull
+ MetricsTimeZoneDetectorState generateMetricsState();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index d163a0e22320..c34a7d37ba24 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -15,7 +15,7 @@
*/
package com.android.server.timezonedetector;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
@@ -195,6 +195,13 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+ /**
+ * The latest manual suggestion received.
+ */
+ @GuardedBy("this")
+ private ReferenceWithHistory<ManualTimeZoneSuggestion> mLatestManualSuggestion =
+ new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
@GuardedBy("this")
private final List<Dumpable> mDumpables = new ArrayList<>();
@@ -286,6 +293,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
// Only store a geolocation suggestion if geolocation detection is currently enabled.
+ // See also clearGeolocationSuggestionIfNeeded().
mLatestGeoLocationSuggestion.set(suggestion);
// Now perform auto time zone detection. The new suggestion may be used to modify the
@@ -324,6 +332,12 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
return false;
}
+ // Record the manual suggestion for debugging / metrics (but only if manual detection is
+ // currently enabled).
+ // Note: This is not used to set the device back to a previous manual suggestion if the user
+ // later disables automatic time zone detection.
+ mLatestManualSuggestion.set(suggestion);
+
setDeviceTimeZoneIfRequired(timeZoneId, cause);
return true;
}
@@ -357,6 +371,28 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
}
+ @Override
+ @NonNull
+ public synchronized MetricsTimeZoneDetectorState generateMetricsState() {
+ int currentUserId = mEnvironment.getCurrentUserId();
+ // Just capture one telephony suggestion: the one that would be used right now if telephony
+ // detection is in use.
+ QualifiedTelephonyTimeZoneSuggestion bestQualifiedTelephonySuggestion =
+ findBestTelephonySuggestion();
+ TelephonyTimeZoneSuggestion telephonySuggestion =
+ bestQualifiedTelephonySuggestion == null
+ ? null : bestQualifiedTelephonySuggestion.suggestion;
+ // A new generator is created each time: we don't want / require consistency.
+ OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+ return MetricsTimeZoneDetectorState.create(
+ tzIdOrdinalGenerator,
+ getConfigurationInternal(currentUserId),
+ mEnvironment.getDeviceTimeZone(),
+ getLatestManualSuggestion(),
+ telephonySuggestion,
+ getLatestGeolocationSuggestion());
+ }
+
private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
int score;
if (suggestion.getZoneId() == null) {
@@ -619,6 +655,11 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
mTimeZoneChangesLog.dump(ipw);
ipw.decreaseIndent(); // level 2
+ ipw.println("Manual suggestion history:");
+ ipw.increaseIndent(); // level 2
+ mLatestManualSuggestion.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
ipw.println("Geolocation suggestion history:");
ipw.increaseIndent(); // level 2
mLatestGeoLocationSuggestion.dump(ipw);
@@ -639,6 +680,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
* A method used to inspect strategy state during tests. Not intended for general use.
*/
@VisibleForTesting
+ public synchronized ManualTimeZoneSuggestion getLatestManualSuggestion() {
+ return mLatestManualSuggestion.get();
+ }
+
+ /**
+ * A method used to inspect strategy state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion(
int slotIndex) {
return mTelephonySuggestionsBySlotIndex.get(slotIndex);
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index c0c9e6d58622..4fa920e5b7d2 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -39,15 +39,15 @@ import java.util.Objects;
*/
class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
- private static final String TAG = LocationTimeZoneManagerService.TAG;
-
@NonNull private final LocationTimeZoneProviderProxy mProxy;
BinderLocationTimeZoneProvider(
+ @NonNull ProviderMetricsLogger providerMetricsLogger,
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
@NonNull LocationTimeZoneProviderProxy proxy) {
- super(threadingDomain, providerName);
+ super(providerMetricsLogger, threadingDomain, providerName,
+ new ZoneInfoDbTimeZoneIdValidator());
mProxy = Objects.requireNonNull(proxy);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 0d1692a8781d..ca4a6408cfbb 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -45,6 +45,7 @@ import com.android.server.FgThread;
import com.android.server.SystemService;
import com.android.server.timezonedetector.ServiceConfigAccessor;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -297,7 +298,9 @@ public class LocationTimeZoneManagerService extends Binder {
R.string.config_primaryLocationTimeZoneProviderPackageName
);
}
- return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
+ ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
+ return new BinderLocationTimeZoneProvider(
+ providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
}
@NonNull
@@ -317,7 +320,9 @@ public class LocationTimeZoneManagerService extends Binder {
R.string.config_secondaryLocationTimeZoneProviderPackageName
);
}
- return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
+ ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
+ return new BinderLocationTimeZoneProvider(
+ providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
}
/** Used for bug triage and in tests to simulate provider events. */
@@ -520,6 +525,12 @@ public class LocationTimeZoneManagerService extends Binder {
}
}
+ static void infoLog(String msg) {
+ if (Log.isLoggable(TAG, Log.INFO)) {
+ Slog.i(TAG, msg);
+ }
+ }
+
static void warnLog(String msg) {
warnLog(msg, null);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index ef2f357b8c3e..cc815dc61886 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -20,6 +20,7 @@ import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESU
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog;
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
@@ -85,6 +86,26 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
/**
+ * Used by {@link LocationTimeZoneProvider} to check if time zone IDs are understood
+ * by the platform.
+ */
+ interface TimeZoneIdValidator {
+
+ /**
+ * Returns whether {@code timeZoneId} is supported by the platform or not.
+ */
+ boolean isValid(@NonNull String timeZoneId);
+ }
+
+ /**
+ * Listener interface used to log provider events for metrics.
+ */
+ interface ProviderMetricsLogger {
+ /** Logs that a provider changed state. */
+ void onProviderStateChanged(@ProviderStateEnum int stateEnum);
+ }
+
+ /**
* Information about the provider's current state.
*/
static class ProviderState {
@@ -336,6 +357,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ @NonNull private final ProviderMetricsLogger mProviderMetricsLogger;
@NonNull final ThreadingDomain mThreadingDomain;
@NonNull final Object mSharedLock;
@NonNull final String mProviderName;
@@ -364,13 +386,19 @@ abstract class LocationTimeZoneProvider implements Dumpable {
// Non-null and effectively final after initialize() is called.
ProviderListener mProviderListener;
+ @NonNull private TimeZoneIdValidator mTimeZoneIdValidator;
+
/** Creates the instance. */
- LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
- @NonNull String providerName) {
+ LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+ @NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName,
+ @NonNull TimeZoneIdValidator timeZoneIdValidator) {
mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
mSharedLock = threadingDomain.getLockObject();
mProviderName = Objects.requireNonNull(providerName);
+ mTimeZoneIdValidator = Objects.requireNonNull(timeZoneIdValidator);
}
/**
@@ -468,6 +496,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
mCurrentState.set(newState);
onSetCurrentState(newState);
if (!Objects.equals(newState, oldState)) {
+ mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum);
if (mStateChangeRecording) {
mRecordedStates.add(newState);
}
@@ -610,6 +639,25 @@ abstract class LocationTimeZoneProvider implements Dumpable {
mThreadingDomain.assertCurrentThread();
Objects.requireNonNull(timeZoneProviderEvent);
+ // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set
+ // the device's time zone. This logic prevents bad time zone IDs entering the time zone
+ // detection logic from third party code.
+ //
+ // An event containing an unknown time zone ID could occur if the provider is using a
+ // different TZDB version than the device. Provider developers are expected to take steps to
+ // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone
+ // rules, or providing IDs based on the device's TZDB version, so this is not considered a
+ // common case.
+ //
+ // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary
+ // enables immediate failover to a secondary provider, one that might provide valid IDs for
+ // the same location, which should provide better behavior than just ignoring the event.
+ if (hasInvalidTimeZones(timeZoneProviderEvent)) {
+ infoLog("event=" + timeZoneProviderEvent + " has unsupported time zones. "
+ + "Replacing it with uncertain event.");
+ timeZoneProviderEvent = TimeZoneProviderEvent.createUncertainEvent();
+ }
+
synchronized (mSharedLock) {
debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName
+ ", timeZoneProviderEvent=" + timeZoneProviderEvent);
@@ -707,6 +755,20 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ private boolean hasInvalidTimeZones(@NonNull TimeZoneProviderEvent event) {
+ if (event.getSuggestion() == null) {
+ return false;
+ }
+
+ for (String timeZone : event.getSuggestion().getTimeZoneIds()) {
+ if (!mTimeZoneIdValidator.isValid(timeZone)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@GuardedBy("mSharedLock")
private void assertIsStarted() {
ProviderState currentState = mCurrentState.get();
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
new file mode 100644
index 000000000000..dfff6f2dd5ae
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import android.annotation.IntRange;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+
+/**
+ * The real implementation of {@link ProviderMetricsLogger} which logs using
+ * {@link FrameworkStatsLog}.
+ */
+public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+
+ @IntRange(from = 0, to = 1)
+ private final int mProviderIndex;
+
+ public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+ mProviderIndex = providerIndex;
+ }
+
+ @Override
+ public void onProviderStateChanged(@ProviderStateEnum int stateEnum) {
+ // TODO(b/172934905): Implement once the atom has landed.
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
new file mode 100644
index 000000000000..cab5ad25c54e
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import android.annotation.NonNull;
+
+import com.android.i18n.timezone.ZoneInfoDb;
+
+class ZoneInfoDbTimeZoneIdValidator implements
+ LocationTimeZoneProvider.TimeZoneIdValidator {
+
+ @Override
+ public boolean isValid(@NonNull String timeZoneId) {
+ return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId);
+ }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a1d2f8a927f7..3ae67aebe7d5 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -3298,7 +3298,11 @@ public final class TvInputManagerService extends SystemService {
values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
sessionToken.toString());
- mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ try{
+ mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ }catch(IllegalArgumentException ex){
+ Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex);
+ }
args.recycle();
break;
}
@@ -3313,7 +3317,11 @@ public final class TvInputManagerService extends SystemService {
values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
sessionToken.toString());
- mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ try{
+ mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ }catch(IllegalArgumentException ex){
+ Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex);
+ }
args.recycle();
break;
}
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index b2db9f5af07e..8dcc547508ec 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -23,7 +23,6 @@ import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
@@ -115,33 +114,61 @@ public class UnderlyingNetworkTracker {
getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
updateSubIdsAndCellularRequests();
- // register Network-selection request used to decide selected underlying Network
+ // Register Network-selection request used to decide selected underlying Network. All
+ // underlying networks must be VCN managed in order to be used.
mConnectivityManager.requestBackgroundNetwork(
- getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback);
+ getBaseNetworkRequest(true /* requireVcnManaged */).build(),
+ mHandler,
+ mRouteSelectionCallback);
}
private NetworkRequest getWifiNetworkRequest() {
- return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ // Request exclusively VCN managed networks to ensure that we only ever keep carrier wifi
+ // alive.
+ return getBaseNetworkRequest(true /* requireVcnManaged */)
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
}
private NetworkRequest getCellNetworkRequestForSubId(int subId) {
- return getNetworkRequestBase()
+ // Do not request NOT_VCN_MANAGED to ensure that the TelephonyNetworkFactory has a
+ // fulfillable request to bring up underlying cellular Networks even if the VCN is already
+ // connected.
+ return getBaseNetworkRequest(false /* requireVcnManaged */)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
}
- private NetworkRequest.Builder getNetworkRequestBase() {
- NetworkRequest.Builder requestBase = new NetworkRequest.Builder();
- for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) {
+ /**
+ * Builds and returns a NetworkRequest builder common to all Underlying Network requests
+ *
+ * <p>A NetworkRequest may either (1) Require the presence of a capability by using
+ * addCapability(), (2) require the absence of a capability using unwanted capabilities, or (3)
+ * allow any state. Underlying networks are never desired to have the NOT_VCN_MANAGED
+ * capability, and only cases (2) and (3) are used.
+ *
+ * @param requireVcnManaged whether the underlying network is required to be VCN managed to
+ * match this request. If {@code true}, the NOT_VCN_MANAGED capability will be set as
+ * unwanted. Else, the NOT_VCN_MANAGED capability will be removed, and any state is
+ * acceptable.
+ */
+ private NetworkRequest.Builder getBaseNetworkRequest(boolean requireVcnManaged) {
+ NetworkRequest.Builder requestBase =
+ new NetworkRequest.Builder()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ for (int capability : mRequiredUnderlyingNetworkCapabilities) {
requestBase.addCapability(capability);
}
- return requestBase
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ if (requireVcnManaged) {
+ requestBase.addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ return requestBase;
}
/**
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index c55913e2e547..3f74938005a7 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -299,9 +299,7 @@ public class Vcn extends Handler {
for (VcnGatewayConnectionConfig gatewayConnectionConfig :
mConfig.getGatewayConnectionConfigs()) {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
- Slog.v(
- getLogTag(),
- "Bringing up new VcnGatewayConnection for request " + request.requestId);
+ Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request);
final VcnGatewayConnection vcnGatewayConnection =
mDeps.newVcnGatewayConnection(
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 15429f455d6e..69a153f79a1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
@@ -59,6 +60,7 @@ import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.vcn.VcnControlPlaneIkeConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
@@ -1348,7 +1350,7 @@ public class VcnGatewayConnection extends StateMachine {
mIkeSession = null;
}
- mIkeSession = buildIkeSession();
+ mIkeSession = buildIkeSession(mUnderlying.network);
}
@Override
@@ -1726,6 +1728,7 @@ public class VcnGatewayConnection extends StateMachine {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addTransportType(TRANSPORT_CELLULAR);
+ builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
builder.addCapability(NET_CAPABILITY_NOT_CONGESTED);
builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -1939,23 +1942,29 @@ public class VcnGatewayConnection extends StateMachine {
new EventDisconnectRequestedInfo(reason, shouldQuit));
}
- private IkeSessionParams buildIkeParams() {
- // TODO: Implement this once IkeSessionParams is persisted
- return null;
+ private IkeSessionParams buildIkeParams(@NonNull Network network) {
+ final VcnControlPlaneIkeConfig controlPlaneConfig =
+ (VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
+ final IkeSessionParams.Builder builder =
+ new IkeSessionParams.Builder(controlPlaneConfig.getIkeSessionParams());
+ builder.setConfiguredNetwork(network);
+
+ return builder.build();
}
private ChildSessionParams buildChildParams() {
- // TODO: Implement this once IkeSessionParams is persisted
- return null;
+ final VcnControlPlaneIkeConfig controlPlaneConfig =
+ (VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
+ return controlPlaneConfig.getChildSessionParams();
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- VcnIkeSession buildIkeSession() {
+ VcnIkeSession buildIkeSession(@NonNull Network network) {
final int token = ++mCurrentToken;
return mDeps.newIkeSession(
mVcnContext,
- buildIkeParams(),
+ buildIkeParams(network),
buildChildParams(),
new IkeSessionCallbackImpl(token),
new VcnChildSessionCallback(token));
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 389326769096..b90408fe5371 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.IVibratorManager;
import android.os.CombinedVibrationEffect;
@@ -35,9 +36,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.FrameworkStatsLog;
-import com.google.android.collect.Lists;
-
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
@@ -47,11 +48,16 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
private static final boolean DEBUG = false;
/**
- * Extra timeout added to the end of each synced vibration step as a timeout for the callback
- * wait, to ensure it finishes even when callbacks from individual vibrators are lost.
+ * Extra timeout added to the end of each vibration step to ensure it finishes even when
+ * vibrator callbacks are lost.
*/
private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
+ /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
+ private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
+
+ private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
+
/** Callbacks for playing a {@link Vibration}. */
interface VibrationCallbacks {
@@ -83,13 +89,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
private final IBatteryStats mBatteryStatsService;
private final Vibration mVibration;
private final VibrationCallbacks mCallbacks;
- private final SparseArray<VibratorController> mVibrators;
+ private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
+ private final StepQueue mStepQueue = new StepQueue();
- @GuardedBy("mLock")
- @Nullable
- private VibrateStep mCurrentVibrateStep;
- @GuardedBy("mLock")
- private boolean mForceStop;
+ private volatile boolean mForceStop;
VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
@@ -102,7 +105,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
mBatteryStatsService = batteryStatsService;
CombinedVibrationEffect effect = vib.getEffect();
- mVibrators = new SparseArray<>();
for (int i = 0; i < availableVibrators.size(); i++) {
if (effect.hasVibrator(availableVibrators.keyAt(i))) {
mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
@@ -110,6 +112,15 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
}
}
+ Vibration getVibration() {
+ return mVibration;
+ }
+
+ @VisibleForTesting
+ SparseArray<VibratorController> getVibrators() {
+ return mVibrators;
+ }
+
@Override
public void binderDied() {
if (DEBUG) {
@@ -136,8 +147,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
/** Cancel current vibration and shuts down the thread gracefully. */
public void cancel() {
+ mForceStop = true;
synchronized (mLock) {
- mForceStop = true;
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancelled");
+ }
mLock.notify();
}
}
@@ -148,11 +162,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
if (DEBUG) {
Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
}
- if (mCurrentVibrateStep != null) {
- for (int i = 0; i < mVibrators.size(); i++) {
- mCurrentVibrateStep.vibratorComplete(mVibrators.keyAt(i));
- }
+ for (int i = 0; i < mVibrators.size(); i++) {
+ mStepQueue.consumeOnVibratorComplete(mVibrators.keyAt(i));
}
+ mLock.notify();
}
}
@@ -162,120 +175,73 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
if (DEBUG) {
Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
}
- if (mCurrentVibrateStep != null) {
- mCurrentVibrateStep.vibratorComplete(vibratorId);
- }
+ mStepQueue.consumeOnVibratorComplete(vibratorId);
+ mLock.notify();
}
}
- Vibration getVibration() {
- return mVibration;
- }
-
- @VisibleForTesting
- SparseArray<VibratorController> getVibrators() {
- return mVibrators;
- }
-
private Vibration.Status playVibration() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
try {
- List<Step> steps = generateSteps(mVibration.getEffect());
- if (steps.isEmpty()) {
- // No vibrator matching any incoming vibration effect.
- return Vibration.Status.IGNORED;
- }
- Vibration.Status status = Vibration.Status.FINISHED;
- final int stepCount = steps.size();
- for (int i = 0; i < stepCount; i++) {
- Step step = steps.get(i);
- synchronized (mLock) {
- if (step instanceof VibrateStep) {
- mCurrentVibrateStep = (VibrateStep) step;
+ CombinedVibrationEffect.Sequential effect = toSequential(mVibration.getEffect());
+ int stepsPlayed = 0;
+
+ synchronized (mLock) {
+ mStepQueue.offer(new StartVibrateStep(effect));
+ Step topOfQueue;
+
+ while ((topOfQueue = mStepQueue.peek()) != null) {
+ long waitTime = topOfQueue.calculateWaitTime();
+ if (waitTime <= 0) {
+ stepsPlayed += mStepQueue.consume();
} else {
- mCurrentVibrateStep = null;
+ try {
+ mLock.wait(waitTime);
+ } catch (InterruptedException e) { }
+ }
+ if (mForceStop) {
+ mStepQueue.cancel();
+ return Vibration.Status.CANCELLED;
}
}
- status = step.play();
- if (status != Vibration.Status.FINISHED) {
- // This step was ignored by the vibrators, probably effects were unsupported.
- break;
- }
- if (mForceStop) {
- break;
- }
- }
- if (mForceStop) {
- return Vibration.Status.CANCELLED;
}
- return status;
+
+ // Some effects might be ignored because the specified vibrator don't exist or doesn't
+ // support the effect. We only report ignored here if nothing was played besides the
+ // StartVibrateStep (which means every attempt to turn on the vibrator was ignored).
+ return stepsPlayed > effect.getEffects().size()
+ ? Vibration.Status.FINISHED : Vibration.Status.IGNORED_UNSUPPORTED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private List<Step> generateSteps(CombinedVibrationEffect effect) {
- if (effect instanceof CombinedVibrationEffect.Sequential) {
- CombinedVibrationEffect.Sequential sequential =
- (CombinedVibrationEffect.Sequential) effect;
- List<Step> steps = new ArrayList<>();
- final int sequentialEffectCount = sequential.getEffects().size();
- for (int i = 0; i < sequentialEffectCount; i++) {
- int delay = sequential.getDelays().get(i);
- if (delay > 0) {
- steps.add(new DelayStep(delay));
- }
- steps.addAll(generateSteps(sequential.getEffects().get(i)));
+ private void noteVibratorOn(long duration) {
+ try {
+ if (duration <= 0) {
+ return;
}
- final int stepCount = steps.size();
- for (int i = 0; i < stepCount; i++) {
- if (steps.get(i) instanceof VibrateStep) {
- return steps;
- }
+ if (duration == Long.MAX_VALUE) {
+ // Repeating duration has started. Report a fixed duration here, noteVibratorOff
+ // should be called when this is cancelled.
+ duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
}
- // No valid vibrate step was generated, ignore effect completely.
- return Lists.newArrayList();
- }
- VibrateStep vibrateStep = null;
- if (effect instanceof CombinedVibrationEffect.Mono) {
- vibrateStep = createVibrateStep(mapToAvailableVibrators(
- ((CombinedVibrationEffect.Mono) effect).getEffect()));
- } else if (effect instanceof CombinedVibrationEffect.Stereo) {
- vibrateStep = createVibrateStep(filterByAvailableVibrators(
- ((CombinedVibrationEffect.Stereo) effect).getEffects()));
- }
- return vibrateStep == null ? Lists.newArrayList() : Lists.newArrayList(vibrateStep);
- }
-
- @Nullable
- private VibrateStep createVibrateStep(SparseArray<VibrationEffect> effects) {
- if (effects.size() == 0) {
- return null;
- }
- if (effects.size() == 1) {
- // Create simplified step that handles a single vibrator.
- return new SingleVibrateStep(mVibrators.get(effects.keyAt(0)), effects.valueAt(0));
- }
- return new SyncedVibrateStep(effects);
- }
-
- private SparseArray<VibrationEffect> mapToAvailableVibrators(VibrationEffect effect) {
- SparseArray<VibrationEffect> mappedEffects = new SparseArray<>(mVibrators.size());
- for (int i = 0; i < mVibrators.size(); i++) {
- mappedEffects.put(mVibrators.keyAt(i), effect);
+ mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+ mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
+ duration);
+ } catch (RemoteException e) {
}
- return mappedEffects;
}
- private SparseArray<VibrationEffect> filterByAvailableVibrators(
- SparseArray<VibrationEffect> effects) {
- SparseArray<VibrationEffect> filteredEffects = new SparseArray<>();
- for (int i = 0; i < effects.size(); i++) {
- if (mVibrators.contains(effects.keyAt(i))) {
- filteredEffects.put(effects.keyAt(i), effects.valueAt(i));
- }
+ private void noteVibratorOff() {
+ try {
+ mBatteryStatsService.noteVibratorOff(mVibration.uid);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+ mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
+ /* duration= */ 0);
+ } catch (RemoteException e) {
}
- return filteredEffects;
}
/**
@@ -306,341 +272,240 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
return timing;
}
- /**
- * Sleeps until given {@code wakeUpTime}.
- *
- * <p>This stops immediately when {@link #cancel()} is called.
- *
- * @return true if waited until wake-up time, false if it was cancelled.
- */
- private boolean waitUntil(long wakeUpTime) {
- synchronized (mLock) {
- long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- while (durationRemaining > 0) {
- try {
- mLock.wait(durationRemaining);
- } catch (InterruptedException e) {
- }
- if (mForceStop) {
- return false;
- }
- durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- }
- }
- return true;
- }
-
- /**
- * Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}.
- *
- * <p>This stops immediately when {@link #cancel()} is called.
- *
- * @return true if finished on vibration complete, false if it was cancelled or timed out.
- */
- private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) {
- synchronized (mLock) {
- long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- while (!step.isVibrationComplete() && durationRemaining > 0) {
- try {
- mLock.wait(durationRemaining);
- } catch (InterruptedException e) {
- }
- if (mForceStop) {
- return false;
- }
- durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
- }
- }
- return step.isVibrationComplete();
- }
-
- private void noteVibratorOn(long duration) {
- try {
- mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
- mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
- duration);
- } catch (RemoteException e) {
- }
- }
-
- private void noteVibratorOff() {
- try {
- mBatteryStatsService.noteVibratorOff(mVibration.uid);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
- mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
- /* duration= */ 0);
- } catch (RemoteException e) {
+ private static CombinedVibrationEffect.Sequential toSequential(CombinedVibrationEffect effect) {
+ if (effect instanceof CombinedVibrationEffect.Sequential) {
+ return (CombinedVibrationEffect.Sequential) effect;
}
+ return (CombinedVibrationEffect.Sequential) CombinedVibrationEffect.startSequential()
+ .addNext(effect)
+ .combine();
}
- /** Represent a single synchronized step while playing a {@link CombinedVibrationEffect}. */
- private interface Step {
- Vibration.Status play();
- }
-
- /** Represent a synchronized vibration step. */
- private interface VibrateStep extends Step {
- /** Callback to notify a vibrator has finished playing a effect. */
- void vibratorComplete(int vibratorId);
-
- /** Returns true if the vibration played by this step is complete. */
- boolean isVibrationComplete();
- }
-
- /** Represent a vibration on a single vibrator. */
- private final class SingleVibrateStep implements VibrateStep {
- private final VibratorController mVibrator;
- private final VibrationEffect mEffect;
-
+ /** Queue for {@link Step Steps}, sorted by their start time. */
+ private final class StepQueue {
@GuardedBy("mLock")
- private boolean mVibrationComplete;
-
- SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
- mVibrator = vibrator;
- mEffect = effect;
- }
+ private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
@GuardedBy("mLock")
- @Override
- public boolean isVibrationComplete() {
- return mVibrationComplete;
+ public void offer(@NonNull Step step) {
+ mNextSteps.offer(step);
}
- @Override
- public void vibratorComplete(int vibratorId) {
- if (mVibrator.getVibratorInfo().getId() != vibratorId) {
- return;
- }
- if (mEffect instanceof VibrationEffect.OneShot
- || mEffect instanceof VibrationEffect.Waveform) {
- // Oneshot and Waveform are controlled by amplitude steps, ignore callbacks.
- return;
- }
- mVibrator.off();
- synchronized (mLock) {
- mVibrationComplete = true;
- mLock.notify();
- }
+ @GuardedBy("mLock")
+ @Nullable
+ public Step peek() {
+ return mNextSteps.peek();
}
- @Override
- public Vibration.Status play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SingleVibrateStep");
- long duration = -1;
- try {
- if (DEBUG) {
- Slog.d(TAG, "SingleVibrateStep starting...");
- }
- long startTime = SystemClock.uptimeMillis();
- duration = vibratePredefined(mEffect);
-
- if (duration > 0) {
- noteVibratorOn(duration);
- // Vibration is playing with no need to control amplitudes, just wait for native
- // callback or timeout.
- if (waitForVibrationComplete(this,
- startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) {
- return Vibration.Status.FINISHED;
- }
- // Timed out or vibration cancelled. Stop vibrator anyway.
- mVibrator.off();
- return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
- }
-
- startTime = SystemClock.uptimeMillis();
- AmplitudeStep amplitudeStep = vibrateWithAmplitude(mEffect, startTime);
- if (amplitudeStep == null) {
- // Vibration could not be played with or without amplitude steps.
- return Vibration.Status.IGNORED_UNSUPPORTED;
- }
-
- duration = mEffect instanceof VibrationEffect.Prebaked
- ? ((VibrationEffect.Prebaked) mEffect).getFallbackEffect().getDuration()
- : mEffect.getDuration();
- if (duration < Long.MAX_VALUE) {
- // Only report vibration stats if we know how long we will be vibrating.
- noteVibratorOn(duration);
- }
- while (amplitudeStep != null) {
- if (!waitUntil(amplitudeStep.startTime)) {
- mVibrator.off();
- return Vibration.Status.CANCELLED;
- }
- amplitudeStep.play();
- amplitudeStep = amplitudeStep.nextStep();
- }
-
- return Vibration.Status.FINISHED;
- } finally {
- if (duration > 0 && duration < Long.MAX_VALUE) {
- noteVibratorOff();
- }
- if (DEBUG) {
- Slog.d(TAG, "SingleVibrateStep done.");
- }
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ /**
+ * Play and remove the step at the top of this queue, and also adds the next steps
+ * generated to be played next.
+ *
+ * @return the number of steps played
+ */
+ @GuardedBy("mLock")
+ public int consume() {
+ Step nextStep = mNextSteps.poll();
+ if (nextStep != null) {
+ mNextSteps.addAll(nextStep.play());
+ return 1;
}
+ return 0;
}
/**
- * Try to vibrate given effect using prebaked or composed predefined effects.
+ * Play and remove the step in this queue that should be anticipated by the vibrator
+ * completion callback.
*
- * @return the duration, in millis, expected for the vibration, or -1 if effect cannot be
- * played with predefined effects.
+ * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
+ * first step found is played by this method, in no particular order.
*/
- private long vibratePredefined(VibrationEffect effect) {
- if (effect instanceof VibrationEffect.Prebaked) {
- VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
- long duration = mVibrator.on(prebaked, mVibration.id);
- if (duration > 0) {
- return duration;
- }
- if (prebaked.getFallbackEffect() != null) {
- return vibratePredefined(prebaked.getFallbackEffect());
+ @GuardedBy("mLock")
+ public void consumeOnVibratorComplete(int vibratorId) {
+ Iterator<Step> it = mNextSteps.iterator();
+ List<Step> nextSteps = EMPTY_STEP_LIST;
+ while (it.hasNext()) {
+ Step step = it.next();
+ if (step.shouldPlayWhenVibratorComplete(vibratorId)) {
+ it.remove();
+ nextSteps = step.play();
+ break;
}
- } else if (effect instanceof VibrationEffect.Composed) {
- VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
- return mVibrator.on(composed, mVibration.id);
}
- // OneShot and Waveform effects require amplitude change after calling vibrator.on.
- return -1;
+ mNextSteps.addAll(nextSteps);
}
/**
- * Try to vibrate given effect using {@link AmplitudeStep} to control vibration amplitude.
+ * Cancel the current queue, clearing all remaining steps.
*
- * @return the {@link AmplitudeStep} to start this vibration, or {@code null} if vibration
- * do not require amplitude control.
+ * <p>This will remove and trigger {@link Step#cancel()} in all steps, in order.
*/
- private AmplitudeStep vibrateWithAmplitude(VibrationEffect effect, long startTime) {
- int vibratorId = mVibrator.getVibratorInfo().getId();
- if (effect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
- return new AmplitudeStep(vibratorId, oneShot, startTime, startTime);
- } else if (effect instanceof VibrationEffect.Waveform) {
- VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
- return new AmplitudeStep(vibratorId, waveform, startTime, startTime);
- } else if (effect instanceof VibrationEffect.Prebaked) {
- VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
- if (prebaked.getFallbackEffect() != null) {
- return vibrateWithAmplitude(prebaked.getFallbackEffect(), startTime);
- }
+ @GuardedBy("mLock")
+ public void cancel() {
+ Step step;
+ while ((step = mNextSteps.poll()) != null) {
+ step.cancel();
}
- return null;
}
}
- /** Represent a synchronized vibration step on multiple vibrators. */
- private final class SyncedVibrateStep implements VibrateStep {
- private final SparseArray<VibrationEffect> mEffects;
- private final long mRequiredCapabilities;
- private final int[] mVibratorIds;
+ /**
+ * Represent a single step for playing a vibration.
+ *
+ * <p>Every step has a start time, which can be used to apply delays between steps while
+ * executing them in sequence.
+ */
+ private abstract class Step implements Comparable<Step> {
+ public final long startTime;
- @GuardedBy("mLock")
- private int mActiveVibratorCounter;
+ Step(long startTime) {
+ this.startTime = startTime;
+ }
- SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
- mEffects = effects;
- mActiveVibratorCounter = mEffects.size();
- mRequiredCapabilities = calculateRequiredSyncCapabilities(effects);
- mVibratorIds = new int[effects.size()];
- for (int i = 0; i < effects.size(); i++) {
- mVibratorIds[i] = effects.keyAt(i);
+ /** Play this step, returning a (possibly empty) list of next steps. */
+ @NonNull
+ public abstract List<Step> play();
+
+ /** Cancel this pending step. */
+ public void cancel() {
+ }
+
+ /**
+ * Return true to play this step right after a vibrator has notified vibration completed,
+ * used to anticipate steps waiting on vibrator callbacks with a timeout.
+ */
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ return false;
+ }
+
+ /** Returns the time in millis to wait before playing this step. */
+ public long calculateWaitTime() {
+ if (startTime == Long.MAX_VALUE) {
+ // This step don't have a predefined start time, it's just marked to be executed
+ // after all other steps have finished.
+ return 0;
}
+ return Math.max(0, startTime - SystemClock.uptimeMillis());
}
- @GuardedBy("mLock")
@Override
- public boolean isVibrationComplete() {
- return mActiveVibratorCounter <= 0;
+ public int compareTo(Step o) {
+ return Long.compare(startTime, o.startTime);
}
+ }
- @Override
- public void vibratorComplete(int vibratorId) {
- VibrationEffect effect = mEffects.get(vibratorId);
- if (effect == null) {
- return;
- }
- if (effect instanceof VibrationEffect.OneShot
- || effect instanceof VibrationEffect.Waveform) {
- // Oneshot and Waveform are controlled by amplitude steps, ignore callbacks.
- return;
- }
- mVibrators.get(vibratorId).off();
- synchronized (mLock) {
- --mActiveVibratorCounter;
- mLock.notify();
- }
+ /**
+ * Starts a sync vibration.
+ *
+ * <p>If this step has successfully started playing a vibration on any vibrator, it will always
+ * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
+ * all their individual steps.
+ *
+ * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
+ * sequential effect isn't finished yet.
+ */
+ private final class StartVibrateStep extends Step {
+ public final CombinedVibrationEffect.Sequential sequentialEffect;
+ public final int currentIndex;
+
+ StartVibrateStep(CombinedVibrationEffect.Sequential effect) {
+ this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
+ }
+
+ StartVibrateStep(long startTime, CombinedVibrationEffect.Sequential effect, int index) {
+ super(startTime);
+ sequentialEffect = effect;
+ currentIndex = index;
}
@Override
- public Vibration.Status play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep");
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
+ List<Step> nextSteps = new ArrayList<>();
long duration = -1;
try {
if (DEBUG) {
- Slog.d(TAG, "SyncedVibrateStep starting...");
+ Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
}
- final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size());
- long startTime = SystemClock.uptimeMillis();
- duration = startVibratingSynced(startTime, nextSteps);
-
- if (duration <= 0) {
- // Vibrate step failed, vibrator could not be turned on for this step.
- return Vibration.Status.IGNORED;
+ CombinedVibrationEffect effect = sequentialEffect.getEffects().get(currentIndex);
+ DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
+ if (effectMapping == null) {
+ // Unable to map effects to vibrators, ignore this step.
+ return nextSteps;
}
+ duration = startVibrating(effectMapping, nextSteps);
noteVibratorOn(duration);
- while (!nextSteps.isEmpty()) {
- AmplitudeStep step = nextSteps.poll();
- if (!waitUntil(step.startTime)) {
- stopAllVibrators();
- return Vibration.Status.CANCELLED;
- }
- step.play();
- AmplitudeStep nextStep = step.nextStep();
- if (nextStep == null) {
- // This vibrator has finished playing the effect for this step.
- synchronized (mLock) {
- mActiveVibratorCounter--;
- }
- } else {
- nextSteps.add(nextStep);
- }
- }
-
- synchronized (mLock) {
- // All OneShot and Waveform effects have finished. Just wait for the other
- // effects to end via native callbacks before finishing this synced step.
- final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT;
- if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
- return Vibration.Status.FINISHED;
- }
-
- // Timed out or vibration cancelled. Stop all vibrators anyway.
- stopAllVibrators();
- return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
- }
} finally {
- if (duration > 0) {
- noteVibratorOff();
- }
- if (DEBUG) {
- Slog.d(TAG, "SyncedVibrateStep done.");
+ // If this step triggered any vibrator then add a finish step to wait for all
+ // active vibrators to finish their individual steps before going to the next.
+ Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep();
+ if (nextStep != null) {
+ nextSteps.add(nextStep);
}
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
+ return nextSteps;
+ }
+
+ /**
+ * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
+ * time this method is called, or null if sequence is complete.
+ */
+ @Nullable
+ private Step nextStep() {
+ int nextIndex = currentIndex + 1;
+ if (nextIndex >= sequentialEffect.getEffects().size()) {
+ return null;
+ }
+ long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
+ long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
+ return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex);
+ }
+
+ /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
+ @Nullable
+ private DeviceEffectMap createEffectToVibratorMapping(
+ CombinedVibrationEffect effect) {
+ if (effect instanceof CombinedVibrationEffect.Mono) {
+ return new DeviceEffectMap((CombinedVibrationEffect.Mono) effect);
+ }
+ if (effect instanceof CombinedVibrationEffect.Stereo) {
+ return new DeviceEffectMap((CombinedVibrationEffect.Stereo) effect);
+ }
+ return null;
}
/**
* Starts playing effects on designated vibrators, in sync.
*
- * @return A positive duration, in millis, to wait for the completion of this effect.
- * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
- * returns the duration of a single run to be used as timeout for callbacks.
+ * @param effectMapping The {@link CombinedVibrationEffect} mapped to this device vibrators
+ * @param nextSteps An output list to accumulate the future {@link Step Steps} created
+ * by this method, typically one for each vibrator that has
+ * successfully started vibrating on this step.
+ * @return The duration, in millis, of the {@link CombinedVibrationEffect}. Repeating
+ * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
+ * have ignored all effects.
*/
- private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
+ private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) {
+ int vibratorCount = effectMapping.size();
+ if (vibratorCount == 0) {
+ // No effect was mapped to any available vibrator.
+ return 0;
+ }
+
+ VibratorOnStep[] steps = new VibratorOnStep[vibratorCount];
+ long vibrationStartTime = SystemClock.uptimeMillis();
+ for (int i = 0; i < vibratorCount; i++) {
+ steps[i] = new VibratorOnStep(vibrationStartTime,
+ mVibrators.get(effectMapping.vibratorIdAt(i)), effectMapping.effectAt(i));
+ }
+
+ if (steps.length == 1) {
+ // No need to prepare and trigger sync effects on a single vibrator.
+ return startVibrating(steps[0], nextSteps);
+ }
+
// This synchronization of vibrators should be executed one at a time, even if we are
// vibrating different sets of vibrators in parallel. The manager can only prepareSynced
// one set of vibrators at a time.
@@ -648,17 +513,24 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
boolean hasPrepared = false;
boolean hasTriggered = false;
try {
- hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities,
- mVibratorIds);
- long timeout = startVibrating(startTime, nextSteps);
+ hasPrepared = mCallbacks.prepareSyncedVibration(
+ effectMapping.getRequiredSyncCapabilities(),
+ effectMapping.getVibratorIds());
+
+ long duration = 0;
+ for (VibratorOnStep step : steps) {
+ duration = Math.max(duration, startVibrating(step, nextSteps));
+ }
- // Check if preparation was successful, otherwise devices area already vibrating
- if (hasPrepared) {
+ // Check if sync was prepared and if any step was accepted by a vibrator,
+ // otherwise there is nothing to trigger here.
+ if (hasPrepared && duration > 0) {
hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
}
- return timeout;
+ return duration;
} finally {
if (hasPrepared && !hasTriggered) {
+ // Trigger has failed or all steps were ignored by the vibrators.
mCallbacks.cancelSyncedVibration();
return 0;
}
@@ -666,261 +538,408 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
}
}
- /**
- * Starts playing effects on designated vibrators.
- *
- * <p>This includes the {@link VibrationEffect.OneShot} and {@link VibrationEffect.Waveform}
- * effects, that should start in sync with all other effects in this step. The waveforms are
- * controlled by {@link AmplitudeStep} added to the {@code nextSteps} queue.
- *
- * @return A positive duration, in millis, to wait for the completion of this effect.
- * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
- * returns the duration of a single run to be used as timeout for callbacks.
- */
- private long startVibrating(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
- long maxDuration = 0;
- for (int i = 0; i < mEffects.size(); i++) {
- VibratorController controller = mVibrators.get(mEffects.keyAt(i));
- VibrationEffect effect = mEffects.valueAt(i);
- maxDuration = Math.max(maxDuration,
- startVibrating(controller, effect, startTime, nextSteps));
+ private long startVibrating(VibratorOnStep step, List<Step> nextSteps) {
+ nextSteps.addAll(step.play());
+ return step.getDuration();
+ }
+ }
+
+ /**
+ * Finish a sync vibration started by a {@link StartVibrateStep}.
+ *
+ * <p>This only plays after all active vibrators steps have finished, and adds a {@link
+ * StartVibrateStep} to the queue if the sequential effect isn't finished yet.
+ */
+ private final class FinishVibrateStep extends Step {
+ public final StartVibrateStep startedStep;
+
+ FinishVibrateStep(StartVibrateStep startedStep) {
+ super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue.
+ this.startedStep = startedStep;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex);
+ }
+ noteVibratorOff();
+ Step nextStep = startedStep.nextStep();
+ return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
- return maxDuration;
+ }
+
+ @Override
+ public void cancel() {
+ noteVibratorOff();
+ }
+ }
+
+ /**
+ * Represent a step turn the vibrator on.
+ *
+ * <p>No other calls to the vibrator is made from this step, so this can be played in between
+ * calls to 'prepare' and 'trigger' for synchronized vibrations.
+ */
+ private final class VibratorOnStep extends Step {
+ public final VibratorController controller;
+ public final VibrationEffect effect;
+ private long mDuration;
+
+ VibratorOnStep(long startTime, VibratorController controller, VibrationEffect effect) {
+ super(startTime);
+ this.controller = controller;
+ this.effect = effect;
}
/**
- * Play a single effect on a single vibrator.
- *
- * @return A positive duration, in millis, to wait for the completion of this effect.
- * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
- * returns the duration of a single run to be used as timeout for callbacks.
+ * Return the duration, in millis, of this effect. Repeating waveforms return {@link
+ * Long#MAX_VALUE}. Zero or negative values indicate the vibrator has ignored this effect.
*/
- private long startVibrating(VibratorController controller, VibrationEffect effect,
- long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
- int vibratorId = controller.getVibratorInfo().getId();
- long duration;
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOnStep");
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId());
+ }
+ List<Step> nextSteps = new ArrayList<>();
+ mDuration = startVibrating(effect, nextSteps);
+ return nextSteps;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private long startVibrating(VibrationEffect effect, List<Step> nextSteps) {
+ final long duration;
+ final long now = SystemClock.uptimeMillis();
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
duration = oneShot.getDuration();
+ // Do NOT set amplitude here. This might be called between prepareSynced and
+ // triggerSynced, so the vibrator is not actually turned on here.
+ // The next steps will handle the amplitude after the vibrator has turned on.
controller.on(duration, mVibration.id);
- nextSteps.add(
- new AmplitudeStep(vibratorId, oneShot, startTime, startTime + duration));
+ nextSteps.add(new VibratorAmplitudeStep(now, controller, oneShot,
+ now + duration + CALLBACKS_EXTRA_TIMEOUT));
} else if (effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
- duration = getVibratorOnDuration(waveform, 0);
- if (duration > 0) {
- // Waveform starts by turning vibrator on. Do it in this sync vibrate step.
- controller.on(duration, mVibration.id);
- }
- nextSteps.add(
- new AmplitudeStep(vibratorId, waveform, startTime, startTime + duration));
+ // Return the full duration of this waveform effect.
+ duration = waveform.getDuration();
+ long onDuration = getVibratorOnDuration(waveform, 0);
+ if (onDuration > 0) {
+ // Do NOT set amplitude here. This might be called between prepareSynced and
+ // triggerSynced, so the vibrator is not actually turned on here.
+ // The next steps will handle the amplitudes after the vibrator has turned on.
+ controller.on(onDuration, mVibration.id);
+ }
+ long offTime = onDuration > 0 ? now + onDuration + CALLBACKS_EXTRA_TIMEOUT : now;
+ nextSteps.add(new VibratorAmplitudeStep(now, controller, waveform, offTime));
} else if (effect instanceof VibrationEffect.Prebaked) {
VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
duration = controller.on(prebaked, mVibration.id);
- if (duration <= 0 && prebaked.getFallbackEffect() != null) {
- return startVibrating(controller, prebaked.getFallbackEffect(), startTime,
- nextSteps);
+ if (duration > 0) {
+ nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
+ controller));
+ } else if (prebaked.getFallbackEffect() != null) {
+ return startVibrating(prebaked.getFallbackEffect(), nextSteps);
}
} else if (effect instanceof VibrationEffect.Composed) {
VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
duration = controller.on(composed, mVibration.id);
+ if (duration > 0) {
+ nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
+ controller));
+ }
} else {
duration = 0;
}
return duration;
}
+ }
- private void stopAllVibrators() {
- for (int vibratorId : mVibratorIds) {
- VibratorController controller = mVibrators.get(vibratorId);
- if (controller != null) {
- controller.off();
- }
- }
+ /**
+ * Represents a step to turn the vibrator off.
+ *
+ * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
+ * and can anticipated by vibrator complete callbacks.
+ */
+ private final class VibratorOffStep extends Step {
+ public final VibratorController controller;
+
+ VibratorOffStep(long startTime, VibratorController controller) {
+ super(startTime);
+ this.controller = controller;
}
- /**
- * Return all capabilities required from the {@link IVibratorManager} to prepare and
- * trigger all given effects in sync.
- *
- * @return {@link IVibratorManager#CAP_SYNC} together with all required
- * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
- */
- private long calculateRequiredSyncCapabilities(SparseArray<VibrationEffect> effects) {
- long prepareCap = 0;
- for (int i = 0; i < effects.size(); i++) {
- VibrationEffect effect = effects.valueAt(i);
- if (effect instanceof VibrationEffect.OneShot
- || effect instanceof VibrationEffect.Waveform) {
- prepareCap |= IVibratorManager.CAP_PREPARE_ON;
- } else if (effect instanceof VibrationEffect.Prebaked) {
- prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
- } else if (effect instanceof VibrationEffect.Composed) {
- prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
- }
- }
- int triggerCap = 0;
- if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
- triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
- }
- if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
- triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
- }
- if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
- triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+ @Override
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ return controller.getVibratorInfo().getId() == vibratorId;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOffStep");
+ try {
+ stopVibrating();
+ return EMPTY_STEP_LIST;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
- return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
}
- /**
- * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
- * different ones, requiring a mixed trigger capability from the vibrator manager for
- * syncing all effects.
- */
- private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
- return (prepareCapabilities & capability) != 0
- && (prepareCapabilities & ~capability) != 0;
+ @Override
+ public void cancel() {
+ stopVibrating();
+ }
+
+ private void stopVibrating() {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+ }
+ controller.off();
}
}
- /** Represent a step to set amplitude on a single vibrator. */
- private final class AmplitudeStep implements Step, Comparable<AmplitudeStep> {
- public final int vibratorId;
+ /** Represents a step to change the amplitude of the vibrator. */
+ private final class VibratorAmplitudeStep extends Step {
+ public final VibratorController controller;
public final VibrationEffect.Waveform waveform;
public final int currentIndex;
- public final long startTime;
- public final long vibratorStopTime;
+ public final long expectedVibratorStopTime;
+
+ private long mNextVibratorStopTime;
- AmplitudeStep(int vibratorId, VibrationEffect.OneShot oneShot,
- long startTime, long vibratorStopTime) {
- this(vibratorId, (VibrationEffect.Waveform) VibrationEffect.createWaveform(
- new long[]{oneShot.getDuration()},
- new int[]{oneShot.getAmplitude()}, /* repeat= */ -1),
- startTime,
- vibratorStopTime);
+ VibratorAmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.OneShot oneShot, long expectedVibratorStopTime) {
+ this(startTime, controller,
+ (VibrationEffect.Waveform) VibrationEffect.createWaveform(
+ new long[]{oneShot.getDuration()}, new int[]{oneShot.getAmplitude()},
+ /* repeat= */ -1),
+ expectedVibratorStopTime);
}
- AmplitudeStep(int vibratorId, VibrationEffect.Waveform waveform,
- long startTime, long vibratorStopTime) {
- this(vibratorId, waveform, /* index= */ 0, startTime, vibratorStopTime);
+ VibratorAmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.Waveform waveform, long expectedVibratorStopTime) {
+ this(startTime, controller, waveform, /* index= */ 0, expectedVibratorStopTime);
}
- AmplitudeStep(int vibratorId, VibrationEffect.Waveform waveform,
- int index, long startTime, long vibratorStopTime) {
- this.vibratorId = vibratorId;
+ VibratorAmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.Waveform waveform, int index, long expectedVibratorStopTime) {
+ super(startTime);
+ this.controller = controller;
this.waveform = waveform;
this.currentIndex = index;
- this.startTime = startTime;
- this.vibratorStopTime = vibratorStopTime;
+ this.expectedVibratorStopTime = expectedVibratorStopTime;
+ mNextVibratorStopTime = expectedVibratorStopTime;
}
@Override
- public Vibration.Status play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ if (controller.getVibratorInfo().getId() == vibratorId) {
+ mNextVibratorStopTime = SystemClock.uptimeMillis();
+ }
+ return false;
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorAmplitudeStep");
try {
if (DEBUG) {
- Slog.d(TAG, "AmplitudeStep starting on vibrator " + vibratorId + "...");
- }
- VibratorController controller = mVibrators.get(vibratorId);
- if (currentIndex < 0) {
- controller.off();
- if (DEBUG) {
- Slog.d(TAG, "Vibrator turned off and finishing");
- }
- return Vibration.Status.FINISHED;
+ long latency = SystemClock.uptimeMillis() - startTime;
+ Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
}
if (waveform.getTimings()[currentIndex] == 0) {
// Skip waveform entries with zero timing.
- return Vibration.Status.FINISHED;
+ return nextSteps();
}
int amplitude = waveform.getAmplitudes()[currentIndex];
if (amplitude == 0) {
- controller.off();
- if (DEBUG) {
- Slog.d(TAG, "Vibrator turned off");
- }
- return Vibration.Status.FINISHED;
+ stopVibrating();
+ return nextSteps();
}
- if (startTime >= vibratorStopTime) {
+ if (startTime >= mNextVibratorStopTime) {
// Vibrator has stopped. Turn vibrator back on for the duration of another
// cycle before setting the amplitude.
long onDuration = getVibratorOnDuration(waveform, currentIndex);
if (onDuration > 0) {
- controller.on(onDuration, mVibration.id);
- if (DEBUG) {
- Slog.d(TAG, "Vibrator turned on for " + onDuration + "ms");
- }
+ startVibrating(onDuration);
+ mNextVibratorStopTime =
+ SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
}
}
- controller.setAmplitude(amplitude);
- if (DEBUG) {
- Slog.d(TAG, "Amplitude changed to " + amplitude);
- }
- return Vibration.Status.FINISHED;
+ changeAmplitude(amplitude);
+ return nextSteps();
} finally {
- if (DEBUG) {
- Slog.d(TAG, "AmplitudeStep done.");
- }
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
@Override
- public int compareTo(AmplitudeStep o) {
- return Long.compare(startTime, o.startTime);
+ public void cancel() {
+ stopVibrating();
}
- /** Return next {@link AmplitudeStep} from this waveform, of {@code null} if finished. */
- @Nullable
- public AmplitudeStep nextStep() {
- if (currentIndex < 0) {
- // Waveform has ended, no more steps to run.
- return null;
+ private void stopVibrating() {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+ }
+ controller.off();
+ mNextVibratorStopTime = SystemClock.uptimeMillis();
+ }
+
+ private void startVibrating(long duration) {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ + duration + "ms");
+ }
+ controller.on(duration, mVibration.id);
+ }
+
+ private void changeAmplitude(int amplitude) {
+ if (DEBUG) {
+ Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
+ + " to " + amplitude);
}
+ controller.setAmplitude(amplitude);
+ }
+
+ @NonNull
+ private List<Step> nextSteps() {
long nextStartTime = startTime + waveform.getTimings()[currentIndex];
int nextIndex = currentIndex + 1;
if (nextIndex >= waveform.getTimings().length) {
nextIndex = waveform.getRepeatIndex();
}
- return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime,
- nextVibratorStopTime());
- }
-
- /** Return next time the vibrator will stop after this step is played. */
- private long nextVibratorStopTime() {
- if (currentIndex < 0 || waveform.getTimings()[currentIndex] == 0
- || startTime < vibratorStopTime) {
- return vibratorStopTime;
+ if (nextIndex < 0) {
+ return Arrays.asList(new VibratorOffStep(nextStartTime, controller));
}
- return startTime + getVibratorOnDuration(waveform, currentIndex);
+ return Arrays.asList(new VibratorAmplitudeStep(nextStartTime, controller, waveform,
+ nextIndex, mNextVibratorStopTime));
}
}
- /** Represent a delay step with fixed duration, that starts counting when it starts playing. */
- private final class DelayStep implements Step {
- private final int mDelay;
+ /**
+ * Map a {@link CombinedVibrationEffect} to the vibrators available on the device.
+ *
+ * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
+ * play all of the effects in sync.
+ */
+ private final class DeviceEffectMap {
+ private final SparseArray<VibrationEffect> mVibratorEffects;
+ private final int[] mVibratorIds;
+ private final long mRequiredSyncCapabilities;
- DelayStep(int delay) {
- mDelay = delay;
+ DeviceEffectMap(CombinedVibrationEffect.Mono mono) {
+ mVibratorEffects = new SparseArray<>(mVibrators.size());
+ mVibratorIds = new int[mVibrators.size()];
+ for (int i = 0; i < mVibrators.size(); i++) {
+ int vibratorId = mVibrators.keyAt(i);
+ mVibratorEffects.put(vibratorId, mono.getEffect());
+ mVibratorIds[i] = vibratorId;
+ }
+ mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
}
- @Override
- public Vibration.Status play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "DelayStep");
- try {
- if (DEBUG) {
- Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
+ DeviceEffectMap(CombinedVibrationEffect.Stereo stereo) {
+ SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
+ mVibratorEffects = new SparseArray<>();
+ for (int i = 0; i < stereoEffects.size(); i++) {
+ int vibratorId = stereoEffects.keyAt(i);
+ if (mVibrators.contains(vibratorId)) {
+ mVibratorEffects.put(vibratorId, stereoEffects.valueAt(i));
}
- if (waitUntil(SystemClock.uptimeMillis() + mDelay)) {
- return Vibration.Status.FINISHED;
- }
- return Vibration.Status.CANCELLED;
- } finally {
- if (DEBUG) {
- Slog.d(TAG, "DelayStep done.");
+ }
+ mVibratorIds = new int[mVibratorEffects.size()];
+ for (int i = 0; i < mVibratorEffects.size(); i++) {
+ mVibratorIds[i] = mVibratorEffects.keyAt(i);
+ }
+ mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+ }
+
+ /**
+ * Return the number of vibrators mapped to play the {@link CombinedVibrationEffect} on this
+ * device.
+ */
+ public int size() {
+ return mVibratorIds.length;
+ }
+
+ /**
+ * Return all capabilities required to play the {@link CombinedVibrationEffect} in
+ * between calls to {@link IVibratorManager#prepareSynced} and
+ * {@link IVibratorManager#triggerSynced}.
+ */
+ public long getRequiredSyncCapabilities() {
+ return mRequiredSyncCapabilities;
+ }
+
+ /** Return all vibrator ids mapped to play the {@link CombinedVibrationEffect}. */
+ public int[] getVibratorIds() {
+ return mVibratorIds;
+ }
+
+ /** Return the id of the vibrator at given index. */
+ public int vibratorIdAt(int index) {
+ return mVibratorEffects.keyAt(index);
+ }
+
+ /** Return the {@link VibrationEffect} at given index. */
+ public VibrationEffect effectAt(int index) {
+ return mVibratorEffects.valueAt(index);
+ }
+
+ /**
+ * Return all capabilities required from the {@link IVibratorManager} to prepare and
+ * trigger all given effects in sync.
+ *
+ * @return {@link IVibratorManager#CAP_SYNC} together with all required
+ * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
+ */
+ private long calculateRequiredSyncCapabilities(SparseArray<VibrationEffect> effects) {
+ long prepareCap = 0;
+ for (int i = 0; i < effects.size(); i++) {
+ VibrationEffect effect = effects.valueAt(i);
+ if (effect instanceof VibrationEffect.OneShot
+ || effect instanceof VibrationEffect.Waveform) {
+ prepareCap |= IVibratorManager.CAP_PREPARE_ON;
+ } else if (effect instanceof VibrationEffect.Prebaked) {
+ prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
+ } else if (effect instanceof VibrationEffect.Composed) {
+ prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
}
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
+ int triggerCap = 0;
+ if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
+ triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
+ }
+ if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
+ triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
+ }
+ if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
+ triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+ }
+ return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
+ }
+
+ /**
+ * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
+ * different ones, requiring a mixed trigger capability from the vibrator manager for
+ * syncing all effects.
+ */
+ private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
+ return (prepareCapabilities & capability) != 0
+ && (prepareCapabilities & ~capability) != 0;
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index eaba083c551c..e3dc70b41c0d 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -54,50 +54,6 @@ final class VibratorController {
void onComplete(int vibratorId, long vibrationId);
}
- /**
- * Initializes the native part of this controller, creating a global reference to given
- * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
- * wrapper is responsible for deleting this pointer by calling the method pointed
- * by {@link #vibratorGetFinalizer()}.
- *
- * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
- * do not hold any strong reference to the instance responsible for deleting the returned
- * pointer, to avoid creating a cyclic GC root reference.
- */
- static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener);
-
- /**
- * Returns pointer to native function responsible for cleaning up the native pointer allocated
- * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}.
- */
- static native long vibratorGetFinalizer();
-
- static native boolean vibratorIsAvailable(long nativePtr);
-
- static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId);
-
- static native void vibratorOff(long nativePtr);
-
- static native void vibratorSetAmplitude(long nativePtr, int amplitude);
-
- static native int[] vibratorGetSupportedEffects(long nativePtr);
-
- static native int[] vibratorGetSupportedPrimitives(long nativePtr);
-
- static native long vibratorPerformEffect(
- long nativePtr, long effect, long strength, long vibrationId);
-
- static native long vibratorPerformComposedEffect(
- long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
-
- static native void vibratorSetExternalControl(long nativePtr, boolean enabled);
-
- static native long vibratorGetCapabilities(long nativePtr);
-
- static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength);
-
- static native void vibratorAlwaysOnDisable(long nativePtr, long id);
-
VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
this(vibratorId, listener, new NativeWrapper());
}
@@ -109,7 +65,8 @@ final class VibratorController {
mNativeWrapper.init(vibratorId, listener);
mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(),
- nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives());
+ nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(),
+ nativeWrapper.getResonantFrequency(), nativeWrapper.getQFactor());
}
/** Register state listener for this vibrator. */
@@ -336,13 +293,47 @@ final class VibratorController {
/** Wrapper around the static-native methods of {@link VibratorController} for tests. */
@VisibleForTesting
public static class NativeWrapper {
+ /**
+ * Initializes the native part of this controller, creating a global reference to given
+ * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
+ * wrapper is responsible for deleting this pointer by calling the method pointed
+ * by {@link #getNativeFinalizer()}.
+ *
+ * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
+ * do not hold any strong reference to the instance responsible for deleting the returned
+ * pointer, to avoid creating a cyclic GC root reference.
+ */
+ private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener);
+
+ /**
+ * Returns pointer to native function responsible for cleaning up the native pointer
+ * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
+ */
+ private static native long getNativeFinalizer();
+ private static native boolean isAvailable(long nativePtr);
+ private static native void on(long nativePtr, long milliseconds, long vibrationId);
+ private static native void off(long nativePtr);
+ private static native void setAmplitude(long nativePtr, int amplitude);
+ private static native int[] getSupportedEffects(long nativePtr);
+ private static native int[] getSupportedPrimitives(long nativePtr);
+ private static native long performEffect(
+ long nativePtr, long effect, long strength, long vibrationId);
+ private static native long performComposedEffect(long nativePtr,
+ VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
+ private static native void setExternalControl(long nativePtr, boolean enabled);
+ private static native long getCapabilities(long nativePtr);
+ private static native void alwaysOnEnable(long nativePtr, long id, long effect,
+ long strength);
+ private static native void alwaysOnDisable(long nativePtr, long id);
+ private static native float getResonantFrequency(long nativePtr);
+ private static native float getQFactor(long nativePtr);
private long mNativePtr = 0;
/** Initializes native controller and allocation registry to destroy native instances. */
public void init(int vibratorId, OnVibrationCompleteListener listener) {
- mNativePtr = VibratorController.vibratorInit(vibratorId, listener);
- long finalizerPtr = VibratorController.vibratorGetFinalizer();
+ mNativePtr = nativeInit(vibratorId, listener);
+ long finalizerPtr = getNativeFinalizer();
if (finalizerPtr != 0) {
NativeAllocationRegistry registry =
@@ -354,65 +345,73 @@ final class VibratorController {
/** Check if the vibrator is currently available. */
public boolean isAvailable() {
- return VibratorController.vibratorIsAvailable(mNativePtr);
+ return isAvailable(mNativePtr);
}
/** Turns vibrator on for given time. */
public void on(long milliseconds, long vibrationId) {
- VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId);
+ on(mNativePtr, milliseconds, vibrationId);
}
/** Turns vibrator off. */
public void off() {
- VibratorController.vibratorOff(mNativePtr);
+ off(mNativePtr);
}
/** Sets the amplitude for the vibrator to run. */
public void setAmplitude(int amplitude) {
- VibratorController.vibratorSetAmplitude(mNativePtr, amplitude);
+ setAmplitude(mNativePtr, amplitude);
}
/** Returns all predefined effects supported by the device vibrator. */
public int[] getSupportedEffects() {
- return VibratorController.vibratorGetSupportedEffects(mNativePtr);
+ return getSupportedEffects(mNativePtr);
}
/** Returns all compose primitives supported by the device vibrator. */
public int[] getSupportedPrimitives() {
- return VibratorController.vibratorGetSupportedPrimitives(mNativePtr);
+ return getSupportedPrimitives(mNativePtr);
}
/** Turns vibrator on to perform one of the supported effects. */
public long perform(long effect, long strength, long vibrationId) {
- return VibratorController.vibratorPerformEffect(
- mNativePtr, effect, strength, vibrationId);
+ return performEffect(mNativePtr, effect, strength, vibrationId);
}
/** Turns vibrator on to perform one of the supported composed effects. */
public long compose(
VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
- return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
- vibrationId);
+ return performComposedEffect(mNativePtr, effect, vibrationId);
}
/** Enabled the device vibrator to be controlled by another service. */
public void setExternalControl(boolean enabled) {
- VibratorController.vibratorSetExternalControl(mNativePtr, enabled);
+ setExternalControl(mNativePtr, enabled);
}
/** Returns all capabilities of the device vibrator. */
public long getCapabilities() {
- return VibratorController.vibratorGetCapabilities(mNativePtr);
+ return getCapabilities(mNativePtr);
}
/** Enable always-on vibration with given id and effect. */
public void alwaysOnEnable(long id, long effect, long strength) {
- VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength);
+ alwaysOnEnable(mNativePtr, id, effect, strength);
}
/** Disable always-on vibration for given id. */
public void alwaysOnDisable(long id) {
- VibratorController.vibratorAlwaysOnDisable(mNativePtr, id);
+ alwaysOnDisable(mNativePtr, id);
+ }
+
+ /** Gets the vibrator's resonant frequency (F0) */
+ public float getResonantFrequency() {
+ return getResonantFrequency(mNativePtr);
+ }
+
+ /** Gets the vibrator's Q factor */
+ public float getQFactor() {
+ return getQFactor(mNativePtr);
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 370d921de2af..75f06e5e9088 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -90,7 +90,6 @@ import android.system.Os;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -2447,8 +2446,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
@Override
public void removeOnLocalColorsChangedListener(
- @NonNull ILocalWallpaperColorConsumer callback, int which, int userId,
- int displayId) throws RemoteException {
+ @NonNull ILocalWallpaperColorConsumer callback, List<RectF> removeAreas, int which,
+ int userId, int displayId) throws RemoteException {
if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
}
@@ -2457,43 +2456,45 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
throw new SecurityException("calling user id does not match");
}
final long identity = Binder.clearCallingIdentity();
- ArrayList<RectF> removeAreas = new ArrayList<>();
- ArrayList<Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>>
- callbacksToRemove = new ArrayList<>();
+ ArrayList<RectF> purgeAreas = new ArrayList<>();
+ IBinder binder = callback.asBinder();
try {
synchronized (mLock) {
- ArraySet<RectF> areas = mLocalColorCallbackAreas.remove(callback.asBinder());
- mLocalColorCallbackDisplayId.remove(callback.asBinder());
- if (areas == null) areas = new ArraySet<>();
- for (RectF area : areas) {
- RemoteCallbackList callbacks = mLocalColorAreaCallbacks.get(area);
- if (callbacks == null) continue;
- callbacksToRemove.add(new Pair<>(callbacks, callback));
- if (callbacks.getRegisteredCallbackCount() == 0) {
- mLocalColorAreaCallbacks.remove(area);
- removeAreas.add(area);
- }
- ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
- if (displayAreas != null) {
- displayAreas.remove(area);
+ ArraySet<RectF> currentAreas = mLocalColorCallbackAreas.get(binder);
+ if (currentAreas == null) return;
+ currentAreas.removeAll(removeAreas);
+ if (currentAreas.size() == 0) {
+ mLocalColorCallbackDisplayId.remove(binder);
+ for (RectF removeArea : removeAreas) {
+ RemoteCallbackList<ILocalWallpaperColorConsumer> remotes =
+ mLocalColorAreaCallbacks.get(removeArea);
+ if (remotes == null) continue;
+ remotes.unregister(callback);
+ if (remotes.getRegisteredCallbackCount() == 0) {
+ mLocalColorAreaCallbacks.remove(removeArea);
+ purgeAreas.add(removeArea);
+ ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
+ if (displayAreas != null) {
+ displayAreas.remove(removeArea);
+ if (displayAreas.size() == 0) {
+ mLocalColorDisplayIdAreas.remove(displayId);
+ }
+ }
+ }
}
}
}
- for (int i = 0; i < callbacksToRemove.size(); i++) {
- Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>
- pair = callbacksToRemove.get(i);
- pair.first.unregister(pair.second);
- }
+
} catch (Exception e) {
// ignore any exception
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (removeAreas.size() == 0) return;
+ if (purgeAreas.size() == 0) return;
IWallpaperEngine engine = getEngine(which, userId, displayId);
if (engine == null) return;
- engine.removeLocalColorsAreas(removeAreas);
+ engine.removeLocalColorsAreas(purgeAreas);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index c830ba9b61dd..c5115b283f0a 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -65,6 +65,7 @@ import android.os.Trace;
import android.service.voice.VoiceInteractionManagerInternal;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
+import android.window.SizeConfigurationBuckets;
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -74,8 +75,6 @@ import com.android.server.Watchdog;
import com.android.server.uri.NeededUriGrants;
import com.android.server.vr.VrManagerInternal;
-import java.util.Arrays;
-
/**
* Server side implementation for the client activity to interact with system.
*
@@ -244,16 +243,14 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
- public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
- ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s",
- token, Arrays.toString(horizontalSizeConfiguration),
- Arrays.toString(verticalSizeConfigurations));
+ public void reportSizeConfigurations(IBinder token,
+ SizeConfigurationBuckets sizeConfigurations) {
+ ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s",
+ token, sizeConfigurations);
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null) {
- r.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations,
- smallestSizeConfigurations);
+ r.setSizeConfigurations(sizeConfigurations);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5446a39fad86..b12ce67832ef 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -84,8 +85,12 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -132,6 +137,7 @@ import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
@@ -205,6 +211,7 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -278,6 +285,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -308,6 +316,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
import android.window.IRemoteTransition;
+import android.window.SizeConfigurationBuckets;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -555,12 +564,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The locusId associated with this activity, if set.
private LocusId mLocusId;
- // These configurations are collected from application's resources based on size-sensitive
- // qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800
- // and drawable-sw400dp will be added to both as 400.
- private int[] mVerticalSizeConfigurations;
- private int[] mHorizontalSizeConfigurations;
- private int[] mSmallestSizeConfigurations;
+ private SizeConfigurationBuckets mSizeConfigurations;
/**
* The precomputed display insets for resolving configuration. It will be non-null if
@@ -656,13 +660,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// naturally.
private boolean mInSizeCompatModeForBounds = false;
- // Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
- // orientation then aspect ratio restrictions are also already respected.
+ // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
+ // for fixed orientation. If not null, they are used as parent container in
+ // resolveSizeCompatModeConfiguration and in a constructor of CompatDisplayInsets. If
+ // letterboxed due to fixed orientation then aspect ratio restrictions are also respected.
// This happens when an activity has fixed orientation which doesn't match orientation of the
// parent because a display is ignoring orientation request or fixed to user rotation.
// See WindowManagerService#getIgnoreOrientationRequest and
// WindowManagerService#getFixedToUserRotation for more context.
- private boolean mIsLetterboxedForFixedOrientationAndAspectRatio = false;
+ @Nullable
+ private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
// activity is not displayed?
// TODO: rename to mNoDisplay
@@ -1070,11 +1077,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+ supportsEnterPipOnTaskSwitch);
}
- if (info.maxAspectRatio != 0) {
- pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
+ if (info.getMaxAspectRatio() != 0) {
+ pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio());
+ }
+ if (info.getMinAspectRatio() != 0) {
+ pw.println(prefix + "minAspectRatio=" + info.getMinAspectRatio());
}
- if (info.minAspectRatio != 0) {
- pw.println(prefix + "minAspectRatio=" + info.minAspectRatio);
+ if (info.getMinAspectRatio() != info.getManifestMinAspectRatio()) {
+ // Log the fact that we've overridden the min aspect ratio from the manifest
+ pw.println(prefix + "manifestMinAspectRatio="
+ + info.getManifestMinAspectRatio());
}
pw.println(prefix + "supportsSizeChanges="
+ ActivityInfo.sizeChangesSupportModeToString(info.supportsSizeChanges()));
@@ -1160,52 +1172,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
info.applicationInfo = aInfo;
}
- private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
- return crossesSizeThreshold(mHorizontalSizeConfigurations, firstDp, secondDp);
- }
-
- private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) {
- return crossesSizeThreshold(mVerticalSizeConfigurations, firstDp, secondDp);
- }
-
- private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) {
- return crossesSizeThreshold(mSmallestSizeConfigurations, firstDp, secondDp);
- }
-
- /**
- * The purpose of this method is to decide whether the activity needs to be relaunched upon
- * changing its size. In most cases the activities don't need to be relaunched, if the resize
- * is small, all the activity content has to do is relayout itself within new bounds. There are
- * cases however, where the activity's content would be completely changed in the new size and
- * the full relaunch is required.
- *
- * The activity will report to us vertical and horizontal thresholds after which a relaunch is
- * required. These thresholds are collected from the application resource qualifiers. For
- * example, if application has layout-w600dp resource directory, then it needs a relaunch when
- * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if
- * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side
- * of the threshold.
- */
- private static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
- int secondDp) {
- if (thresholds == null) {
- return false;
- }
- for (int i = thresholds.length - 1; i >= 0; i--) {
- final int threshold = thresholds[i];
- if ((firstDp < threshold && secondDp >= threshold)
- || (firstDp >= threshold && secondDp < threshold)) {
- return true;
- }
- }
- return false;
- }
-
- void setSizeConfigurations(int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
- mHorizontalSizeConfigurations = horizontalSizeConfiguration;
- mVerticalSizeConfigurations = verticalSizeConfigurations;
- mSmallestSizeConfigurations = smallestSizeConfigurations;
+ void setSizeConfigurations(SizeConfigurationBuckets sizeConfigurations) {
+ mSizeConfigurations = sizeConfigurations;
}
private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
@@ -1511,7 +1479,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
if (windowSurface != null && windowSurface.isValid()) {
- Transaction transaction = getPendingTransaction();
+ Transaction transaction = getSyncTransaction();
transaction.setCornerRadius(windowSurface, cornersRadius);
}
}
@@ -1523,7 +1491,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
layoutLetterbox(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(getPendingTransaction());
+ mLetterbox.applySurfaceChanges(getSyncTransaction());
}
}
@@ -2107,7 +2075,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
if (abort) {
- surface.remove();
+ surface.remove(false /* prepareAnimation */);
}
} else {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s",
@@ -2121,7 +2089,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated,
TaskSnapshot snapshot) {
- if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+ if ((newTask || !processRunning || (taskSwitch && !activityCreated))
+ && !isActivityTypeHome()) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
if (isSnapshotCompatible(snapshot)) {
@@ -2172,7 +2141,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ ActivityRecord.this + " state " + mTransferringSplashScreenState);
if (isTransferringSplashScreen()) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
- // TODO show default exit splash screen animation
removeStartingWindow();
}
}
@@ -2189,6 +2157,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
private boolean transferSplashScreenIfNeeded() {
+ if (!mWmService.mStartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ return false;
+ }
if (!mHandleExitSplashScreen || mStartingSurface == null || mStartingWindow == null
|| mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
return false;
@@ -2258,10 +2229,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// no matter what, remove the starting window.
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
- removeStartingWindow();
+ removeStartingWindowAnimation(false /* prepareAnimation */);
}
void removeStartingWindow() {
+ removeStartingWindowAnimation(true /* prepareAnimation */);
+ }
+
+ void removeStartingWindowAnimation(boolean prepareAnimation) {
if (transferSplashScreenIfNeeded()) {
return;
}
@@ -2272,6 +2247,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Go ahead and cancel the request.
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Clearing startingData for token=%s", this);
mStartingData = null;
+ // Clean surface up since we don't want the window to be added back, so we don't
+ // need to keep the surface to remove it.
+ mStartingSurface = null;
}
return;
}
@@ -2306,7 +2284,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mWmService.mAnimationHandler.post(() -> {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
try {
- surface.remove();
+ surface.remove(prepareAnimation);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when removing starting window", e);
}
@@ -6183,7 +6161,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Remove orphaned starting window.
if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
mStartingWindowState = STARTING_WINDOW_REMOVED;
- removeStartingWindow();
+ removeStartingWindowAnimation(false /* prepareAnimation */);
}
if (isState(INITIALIZING) && !shouldBeVisible(
true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
@@ -6815,8 +6793,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* aspect ratio.
*/
boolean shouldCreateCompatDisplayInsets() {
- if (info.supportsSizeChanges() != ActivityInfo.SIZE_CHANGES_UNSUPPORTED) {
- return false;
+ switch (info.supportsSizeChanges()) {
+ case SIZE_CHANGES_SUPPORTED_METADATA:
+ case SIZE_CHANGES_SUPPORTED_OVERRIDE:
+ return false;
+ case SIZE_CHANGES_UNSUPPORTED_OVERRIDE:
+ return true;
+ default:
+ // Fall through
}
if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
final ActivityRecord root = task != null ? task.getRootActivity() : null;
@@ -6842,7 +6826,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
- private void updateCompatDisplayInsets(@Nullable Rect fixedOrientationBounds) {
+ private void updateCompatDisplayInsets() {
if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
// The override configuration is set only once in size compatibility mode.
return;
@@ -6870,7 +6854,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The role of CompatDisplayInsets is like the override bounds.
mCompatDisplayInsets =
- new CompatDisplayInsets(mDisplayContent, this, fixedOrientationBounds);
+ new CompatDisplayInsets(
+ mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
}
@VisibleForTesting
@@ -6924,8 +6909,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|| windowingMode == WINDOWING_MODE_FULLSCREEN) {
resolveFixedOrientationConfiguration(newParentConfiguration);
}
- final Rect fixedOrientationBounds = isLetterboxedForFixedOrientationAndAspectRatio()
- ? new Rect(resolvedConfig.windowConfiguration.getBounds()) : null;
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
@@ -6945,7 +6928,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (mVisibleRequested) {
- updateCompatDisplayInsets(fixedOrientationBounds);
+ updateCompatDisplayInsets();
}
// TODO(b/175212232): Consolidate position logic from each "resolve" method above here.
@@ -6955,6 +6938,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+
+ // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
+ // size compat mode.
+ if (providesMaxBounds()) {
+ if (DEBUG_CONFIGURATION) {
+ ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
+ + "due to letterboxing from mismatch with parent bounds? %s size compat "
+ + "mode %s", getUid(),
+ resolvedConfig.windowConfiguration.getBounds(), !matchParentBounds(),
+ inSizeCompatMode());
+ }
+ resolvedConfig.windowConfiguration
+ .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
+ }
}
/**
@@ -6966,7 +6963,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* WindowManagerService#getIgnoreOrientationRequest} for more context.
*/
boolean isLetterboxedForFixedOrientationAndAspectRatio() {
- return mIsLetterboxedForFixedOrientationAndAspectRatio;
+ return mLetterboxBoundsForFixedOrientationAndAspectRatio != null;
}
/**
@@ -6977,7 +6974,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* in this methiod.
*/
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
- mIsLetterboxedForFixedOrientationAndAspectRatio = false;
+ mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
if (handlesOrientationChangeFromDescendant()) {
// No need to letterbox because of fixed orientation. Display will handle
// fixed-orientation requests.
@@ -7018,8 +7015,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
// order to use the extra available space.
- final float maxAspectRatio = info.maxAspectRatio;
- final float minAspectRatio = info.minAspectRatio;
+ final float maxAspectRatio = info.getMaxAspectRatio();
+ final float minAspectRatio = info.getMinAspectRatio();
if (aspect > maxAspectRatio && maxAspectRatio != 0) {
aspect = maxAspectRatio;
} else if (aspect < minAspectRatio) {
@@ -7054,7 +7051,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
- mIsLetterboxedForFixedOrientationAndAspectRatio = true;
+ mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
}
/**
@@ -7256,21 +7253,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The rest of the condition is that only one side is smaller than the container, but it
// still needs to exclude the cases where the size is limited by the fixed aspect ratio.
- if (info.maxAspectRatio > 0) {
+ if (info.getMaxAspectRatio() > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
- if (aspectRatio >= info.maxAspectRatio) {
+ if (aspectRatio >= info.getMaxAspectRatio()) {
// The current size has reached the max aspect ratio.
return false;
}
}
- if (info.minAspectRatio > 0) {
+ if (info.getMinAspectRatio() > 0) {
// The activity should have at least the min aspect ratio, so this checks if the
// container still has available space to provide larger aspect ratio.
final float containerAspectRatio =
(0.5f + Math.max(containerAppWidth, containerAppHeight))
/ Math.min(containerAppWidth, containerAppHeight);
- if (containerAspectRatio <= info.minAspectRatio) {
+ if (containerAspectRatio <= info.getMinAspectRatio()) {
// The long side has reached the parent.
return false;
}
@@ -7296,6 +7293,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return super.getBounds();
}
+ @Override
+ public boolean providesMaxBounds() {
+ // System and SystemUI should always be able to access the physical display bounds,
+ // so do not provide it with the overridden maximum bounds.
+ // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
+ if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
+ getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
+ return false;
+ }
+ // Max bounds should be sandboxed where an activity is letterboxed (activity bounds will be
+ // smaller than task bounds) or put in size compat mode.
+ return !matchParentBounds() || inSizeCompatMode();
+ }
+
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
@@ -7423,9 +7434,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds) {
- final float maxAspectRatio = info.maxAspectRatio;
+ final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
- final float minAspectRatio = info.minAspectRatio;
+ final float minAspectRatio = info.getMinAspectRatio();
if (task == null || rootTask == null
|| (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
@@ -7586,6 +7597,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
+ // It's possible that resolveOverrideConfiguration was called before mVisibleRequested
+ // became true and mCompatDisplayInsets may not have been created so ensure
+ // that mCompatDisplayInsets is created here.
+ if (mVisibleRequested) {
+ updateCompatDisplayInsets();
+ }
return true;
}
@@ -7735,26 +7752,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Determine what has changed. May be nothing, if this is a config that has come back from
// the app after going idle. In that case we just want to leave the official config object
// now in the activity and do nothing else.
- final Configuration currentConfig = getConfiguration();
- int changes = lastReportedConfig.diff(currentConfig);
- // We don't want to use size changes if they don't cross boundaries that are important to
- // the app.
- if ((changes & CONFIG_SCREEN_SIZE) != 0) {
- final boolean crosses = crossesHorizontalSizeThreshold(lastReportedConfig.screenWidthDp,
- currentConfig.screenWidthDp)
- || crossesVerticalSizeThreshold(lastReportedConfig.screenHeightDp,
- currentConfig.screenHeightDp);
- if (!crosses) {
- changes &= ~CONFIG_SCREEN_SIZE;
- }
- }
- if ((changes & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
- final int oldSmallest = lastReportedConfig.smallestScreenWidthDp;
- final int newSmallest = currentConfig.smallestScreenWidthDp;
- if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
- changes &= ~CONFIG_SMALLEST_SCREEN_SIZE;
- }
- }
+ int changes = lastReportedConfig.diff(getConfiguration());
+ changes = SizeConfigurationBuckets.filterDiff(
+ changes, lastReportedConfig, getConfiguration(), mSizeConfigurations);
// We don't want window configuration to cause relaunches.
if ((changes & CONFIG_WINDOW_CONFIGURATION) != 0) {
changes &= ~CONFIG_WINDOW_CONFIGURATION;
@@ -8214,6 +8214,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(PROC_ID, app.getPid());
}
proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
+ proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c6cece3d2b23..18e55527f1e1 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1429,6 +1429,7 @@ class ActivityStarter {
+ "; allowBackgroundActivityStart: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask())
+ "]");
// log aborted activity start to TRON
if (mService.isActivityStartsLoggingEnabled()) {
@@ -2707,7 +2708,8 @@ class ActivityStarter {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
- launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
+ launchFlags &=
+ ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
break;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d4eedf153c24..52d110c95e36 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -557,7 +557,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
- boolean mSupportsNonResizableMultiWindow;
+ volatile boolean mSupportsNonResizableMultiWindow;
final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -3432,6 +3432,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public boolean supportsNonResizableMultiWindow() {
+ return mSupportsNonResizableMultiWindow;
+ }
+
+ @Override
public boolean updateConfiguration(Configuration values) {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5b685b4a0499..99289e0139b0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1305,7 +1305,7 @@ public class AppTransition implements Dump {
if (isTransitionSet()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
- mRemoteAnimationController = new RemoteAnimationController(mService,
+ mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
remoteAnimationAdapter, mHandler);
}
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index ab1ed67d4cf9..71a10df34d30 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -70,9 +70,10 @@ class BackgroundLaunchProcessController {
}
boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
- boolean appSwitchAllowed, boolean isCheckingForFgsStart, boolean hasVisibleActivities,
- boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime,
- long lastActivityLaunchTime, long lastActivityFinishTime) {
+ boolean appSwitchAllowed, boolean isCheckingForFgsStart,
+ boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
+ long lastStopAppSwitchesTime, long lastActivityLaunchTime,
+ long lastActivityFinishTime) {
// If app switching is not allowed, we ignore all the start activity grace period
// exception so apps cannot start itself in onPause() after pressing home button.
if (appSwitchAllowed) {
@@ -110,7 +111,7 @@ class BackgroundLaunchProcessController {
return true;
}
// Allow if the caller has an activity in any foreground task.
- if (appSwitchAllowed && hasVisibleActivities) {
+ if (appSwitchAllowed && hasActivityInVisibleTask) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process has activity in foreground task");
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index 13295e8aca02..128d452c3018 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -22,12 +22,17 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.view.ICrossWindowBlurEnabledListener;
+import com.android.internal.annotations.GuardedBy;
+
final class BlurController {
private final RemoteCallbackList<ICrossWindowBlurEnabledListener>
mBlurEnabledListeners = new RemoteCallbackList<>();
private final Object mLock = new Object();
+ @GuardedBy("mLock")
boolean mBlurEnabled;
+ @GuardedBy("mLock")
+ boolean mBlurForceDisabled;
BlurController() {
mBlurEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
@@ -46,19 +51,24 @@ final class BlurController {
mBlurEnabledListeners.unregister(listener);
}
- private void updateBlurEnabled() {
- // TODO: add other factors disabling blurs
- final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
+ void setForceCrossWindowBlurDisabled(boolean disable) {
synchronized (mLock) {
- if (mBlurEnabled == newEnabled) {
- return;
- }
- mBlurEnabled = newEnabled;
- notifyBlurEnabledChanged(newEnabled);
+ mBlurForceDisabled = disable;
+ updateBlurEnabledLocked();
+ }
+
+ }
+
+ private void updateBlurEnabledLocked() {
+ final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled;
+ if (mBlurEnabled == newEnabled) {
+ return;
}
+ mBlurEnabled = newEnabled;
+ notifyBlurEnabledChangedLocked(newEnabled);
}
- private void notifyBlurEnabledChanged(boolean enabled) {
+ private void notifyBlurEnabledChangedLocked(boolean enabled) {
int i = mBlurEnabledListeners.beginBroadcast();
while (i > 0) {
i--;
@@ -71,6 +81,4 @@ final class BlurController {
}
mBlurEnabledListeners.finishBroadcast();
}
-
-
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 62a00802896f..8fbe1775fd19 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -367,8 +367,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* Returns {@code true} if this {@link ConfigurationContainer} provides the maximum bounds to
* its child {@link ConfigurationContainer}s. Returns {@code false}, otherwise.
* <p>
- * The maximum bounds is how large a window can be expanded. Currently only
- * {@link DisplayContent} and {@link DisplayArea} effect this property.
+ * The maximum bounds is how large a window can be expanded.
* </p>
*/
protected boolean providesMaxBounds() {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 5ccf576e1099..9855ea50c83d 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -451,7 +451,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
if (organizer == null) return;
- migrateToNewSurfaceControl();
+ migrateToNewSurfaceControl(getSyncTransaction());
mOrganizerController.onDisplayAreaVanished(organizer, this);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1d45c6e1a371..95b2b5d088a0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -131,7 +131,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
@@ -1811,11 +1810,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
w.mReportOrientationChanged = true;
}, true /* traverseTopToBottom */);
- if (rotateSeamlessly) {
- mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
- this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
- }
-
for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
final WindowManagerService.RotationWatcher rotationWatcher
= mWmService.mRotationWatchers.get(i);
@@ -1868,6 +1862,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Update application display metrics.
final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
+ final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
@@ -1884,6 +1879,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
mDisplayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;
+ mDisplayInfo.roundedCorners = roundedCorners;
mDisplayInfo.getAppMetrics(mDisplayMetrics);
if (mDisplayScalingDisabled) {
mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;
@@ -3432,7 +3428,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
- assignChildLayers(getPendingTransaction());
+ assignChildLayers(getSyncTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
@@ -5665,16 +5661,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
return false; /* continue */
}
- if (taskId != INVALID_TASK_ID) {
+ if (taskId == INVALID_TASK_ID) {
+ if (!nextWindow.canReceiveKeys()) {
+ return false; /* continue */
+ }
+ } else {
Task task = nextWindow.getTask();
if (task == null || !task.isTaskId(taskId)) {
return false; /* continue */
}
}
- if (!nextWindow.canReceiveKeys()) {
- return false; /* continue */
- }
- return true; /* stop */
+
+ return true; /* stop, match found */
}
});
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 7e16c22ee15e..1262dee68b74 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
-import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
+import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,7 +34,6 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -45,16 +46,21 @@ import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.displayhash.DisplayHashParams;
import android.service.displayhash.DisplayHasherService;
import android.service.displayhash.IDisplayHasherService;
+import android.util.Size;
import android.util.Slog;
import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -78,12 +84,15 @@ public class DisplayHashController {
private final Context mContext;
/**
- * Lock used for the cached {@link #mHashAlgorithms} array
+ * Lock used for the cached {@link #mDisplayHashAlgorithms} map
*/
- private final Object mHashAlgorithmsLock = new Object();
+ private final Object mDisplayHashAlgorithmsLock = new Object();
- @GuardedBy("mHashingAlgorithmsLock")
- private String[] mHashAlgorithms;
+ /**
+ * The cached map of display hash algorithms to the {@link DisplayHashParams}
+ */
+ @GuardedBy("mDisplayHashAlgorithmsLock")
+ private Map<String, DisplayHashParams> mDisplayHashAlgorithms;
private final Handler mHandler;
@@ -104,34 +113,8 @@ public class DisplayHashController {
}
String[] getSupportedHashAlgorithms() {
- // We have a separate lock for the hashing algorithm array since it doesn't need to make
- // the request through the service connection. Instead, we have a lock to ensure we can
- // properly cache the hashing algorithms array so we don't need to call into the
- // ExtServices process for each request.
- synchronized (mHashAlgorithmsLock) {
- // Already have cached values
- if (mHashAlgorithms != null) {
- return mHashAlgorithms;
- }
-
- final ServiceInfo serviceInfo = getServiceInfo();
- if (serviceInfo == null) return null;
-
- final PackageManager pm = mContext.getPackageManager();
- final Resources res;
- try {
- res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Error getting application resources for " + serviceInfo, e);
- return null;
- }
-
- final int resourceId = serviceInfo.metaData.getInt(
- SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
- mHashAlgorithms = res.getStringArray(resourceId);
-
- return mHashAlgorithms;
- }
+ Map<String, DisplayHashParams> displayHashAlgorithms = getDisplayHashAlgorithms();
+ return displayHashAlgorithms.keySet().toArray(new String[0]);
}
@Nullable
@@ -148,13 +131,76 @@ public class DisplayHashController {
return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
- void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
+ private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
connectAndRun(
service -> service.generateDisplayHash(mSalt, buffer, bounds, hashAlgorithm,
callback));
}
+ void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
+ Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+ final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
+ DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
+ if (displayHashParams == null) {
+ Slog.w(TAG, "Failed to generateDisplayHash. Invalid hashAlgorithm");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM);
+ return;
+ }
+
+ Size size = displayHashParams.getBufferSize();
+ if (size != null && (size.getWidth() > 0 || size.getHeight() > 0)) {
+ args.setFrameScale((float) size.getWidth() / boundsInWindow.width(),
+ (float) size.getHeight() / boundsInWindow.height());
+ }
+
+ args.setGrayscale(displayHashParams.isGrayscaleBuffer());
+
+ SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
+ SurfaceControl.captureLayers(args.build());
+ if (screenshotHardwareBuffer == null
+ || screenshotHardwareBuffer.getHardwareBuffer() == null) {
+ Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
+ return;
+ }
+
+ generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow,
+ hashAlgorithm, callback);
+ }
+
+ private Map<String, DisplayHashParams> getDisplayHashAlgorithms() {
+ // We have a separate lock for the hashing params to ensure we can properly cache the
+ // hashing params so we don't need to call into the ExtServices process for each request.
+ synchronized (mDisplayHashAlgorithmsLock) {
+ if (mDisplayHashAlgorithms != null) {
+ return mDisplayHashAlgorithms;
+ }
+
+ final SyncCommand syncCommand = new SyncCommand();
+ Bundle results = syncCommand.run((service, remoteCallback) -> {
+ try {
+ service.getDisplayHashAlgorithms(remoteCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke getDisplayHashAlgorithms command", e);
+ }
+ });
+
+ mDisplayHashAlgorithms = new HashMap<>(results.size());
+ for (String key : results.keySet()) {
+ mDisplayHashAlgorithms.put(key, results.getParcelable(key));
+ }
+
+ return mDisplayHashAlgorithms;
+ }
+ }
+
+ void sendDisplayHashError(RemoteCallback callback, int errorCode) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
+ callback.sendResult(bundle);
+ }
+
/**
* Calculate the bounds to generate the hash for. This takes into account window transform,
* magnification, and display bounds.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 32152ec85493..d929d50919c6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -448,7 +448,8 @@ public class DisplayPolicy {
final Looper looper = UiThread.getHandler().getLooper();
mHandler = new PolicyHandler(looper);
- mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
+ // TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context.
+ mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
new SystemGesturesPointerEventListener.Callbacks() {
@Override
public void onSwipeFromTop() {
@@ -1410,9 +1411,9 @@ public class DisplayPolicy {
boolean localClient) {
final InsetsState state =
mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
- final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
- outInsetsState.set(state, inSizeCompatMode || localClient);
- if (inSizeCompatMode) {
+ final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken);
+ outInsetsState.set(state, hasCompatScale || localClient);
+ if (hasCompatScale) {
final float compatScale = windowToken != null
? windowToken.getSizeCompatScale()
: mDisplayContent.mCompatibleScreenScale;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 63cb38a59349..5df1355f3460 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -567,7 +567,7 @@ public class DisplayRotation {
}
mDisplayContent.forAllWindows(w -> {
if (w.mSeamlesslyRotated) {
- w.finishSeamlessRotation(false /* timeout */);
+ w.cancelSeamlessRotation();
w.mSeamlesslyRotated = false;
}
}, true /* traverseTopToBottom */);
@@ -670,24 +670,6 @@ public class DisplayRotation {
}
}
- void onSeamlessRotationTimeout() {
- final boolean[] isLayoutNeeded = { false };
-
- mDisplayContent.forAllWindows(w -> {
- if (!w.mSeamlesslyRotated) {
- return;
- }
- isLayoutNeeded[0] = true;
- w.setDisplayLayoutNeeded();
- w.finishSeamlessRotation(true /* timeout */);
- markForSeamlessRotation(w, false /* seamlesslyRotated */);
- }, true /* traverseTopToBottom */);
-
- if (isLayoutNeeded[0]) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
-
/**
* Returns the animation to run for a rotation transition based on the top fullscreen windows
* {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 999c5859ba1d..62e4a8547600 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -16,12 +16,15 @@
package com.android.server.wm;
+import static java.lang.Integer.toHexString;
+
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
@@ -32,6 +35,9 @@ import java.util.ArrayList;
class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
implements IBinder.DeathRecipient {
+ private static final String TAG = "DragAndDrop";
+ private static final boolean DEBUG = false;
+
private final WindowManagerGlobalLock mGlobalLock;
private final int mSourceUid;
private final String mTargetPackage;
@@ -43,7 +49,7 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
private IBinder mActivityToken = null;
private IBinder mPermissionOwnerToken = null;
- private IBinder mTransientToken = null;
+ private IBinder mAppToken = null;
DragAndDropPermissionsHandler(WindowManagerGlobalLock lock, ClipData clipData, int sourceUid,
String targetPackage, int mode, int sourceUserId, int targetUserId) {
@@ -62,6 +68,10 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
if (mActivityToken != null || mPermissionOwnerToken != null) {
return;
}
+ if (DEBUG) {
+ Log.d(TAG, this + ": taking permissions bound to activity: "
+ + toHexString(activityToken.hashCode()));
+ }
mActivityToken = activityToken;
// Will throw if Activity is not found.
@@ -84,14 +94,18 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
}
@Override
- public void takeTransient(IBinder transientToken) throws RemoteException {
+ public void takeTransient(IBinder appToken) throws RemoteException {
if (mActivityToken != null || mPermissionOwnerToken != null) {
return;
}
+ if (DEBUG) {
+ Log.d(TAG, this + ": taking permissions bound to app process: "
+ + toHexString(appToken.hashCode()));
+ }
mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
.newUriPermissionOwner("drop");
- mTransientToken = transientToken;
- mTransientToken.linkToDeath(this, 0);
+ mAppToken = appToken;
+ mAppToken.linkToDeath(this, 0);
doTake(mPermissionOwnerToken);
}
@@ -112,11 +126,17 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
} finally {
mActivityToken = null;
}
+ if (DEBUG) {
+ Log.d(TAG, this + ": releasing activity-bound permissions");
+ }
} else {
permissionOwner = mPermissionOwnerToken;
mPermissionOwnerToken = null;
- mTransientToken.unlinkToDeath(this, 0);
- mTransientToken = null;
+ mAppToken.unlinkToDeath(this, 0);
+ mAppToken = null;
+ if (DEBUG) {
+ Log.d(TAG, this + ": releasing process-bound permissions");
+ }
}
UriGrantsManagerInternal ugm = LocalServices.getService(UriGrantsManagerInternal.class);
@@ -139,6 +159,9 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
@Override
public void binderDied() {
+ if (DEBUG) {
+ Log.d(TAG, this + ": app process died: " + toHexString(mAppToken.hashCode()));
+ }
try {
release();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 627af9149fe5..1120a074aa8c 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -182,8 +182,6 @@ class DragDropController {
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
}
-
- mDragState.notifyLocationLocked(touchX, touchY);
} finally {
if (surface != null) {
surface.release();
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0b3c065e0e73..08d5e800a808 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -515,50 +515,6 @@ class DragState {
mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply();
ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: pos=(%d,%d)", mSurfaceControl,
(int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY));
-
- notifyLocationLocked(x, y);
- }
-
- void notifyLocationLocked(float x, float y) {
- // Tell the affected window
- WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
- if (touchedWin != null && !isWindowNotified(touchedWin)) {
- // The drag point is over a window which was not notified about a drag start.
- // Pretend it's over empty space.
- touchedWin = null;
- }
-
- try {
- final int myPid = Process.myPid();
-
- // have we dragged over a new window?
- if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow);
- }
- // force DRAG_EXITED_EVENT if appropriate
- DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
- 0, 0, 0, 0, null, null, null, null, null, false);
- mTargetWindow.mClient.dispatchDragEvent(evt);
- if (myPid != mTargetWindow.mSession.mPid) {
- evt.recycle();
- }
- }
- if (touchedWin != null) {
- if (false && DEBUG_DRAG) {
- Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin);
- }
- DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
- x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null, false);
- touchedWin.mClient.dispatchDragEvent(evt);
- if (myPid != touchedWin.mSession.mPid) {
- evt.recycle();
- }
- }
- } catch (RemoteException e) {
- Slog.w(TAG_WM, "can't send drag notification to windows");
- }
- mTargetWindow = touchedWin;
}
/**
diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
index a1e3ac71a3b6..aa7317022794 100644
--- a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
@@ -37,10 +37,15 @@ public class FixedRotationAnimationController extends FadeAnimationController {
super(displayContent);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
mStatusBar = displayPolicy.getStatusBar();
- // Do not animate movable navigation bar (e.g. non-gesture mode).
+
+ final RecentsAnimationController controller =
+ displayContent.mWmService.getRecentsAnimationController();
+ final boolean navBarControlledByRecents =
+ controller != null && controller.isNavigationBarAttachedToApp();
+ // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation bar
+ // is currently controlled by recents animation.
mNavigationBar = !displayPolicy.navigationBarCanMove()
- ? displayPolicy.getNavigationBar()
- : null;
+ && !navBarControlledByRecents ? displayPolicy.getNavigationBar() : null;
}
/** Applies show animation on the previously hidden window tokens. */
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index c6c7fe083b16..45c4233b40aa 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -93,7 +93,6 @@ class InsetsSourceProvider {
private boolean mServerVisible;
private boolean mSeamlessRotating;
- private long mFinishSeamlessRotateFrameNumber = -1;
private final boolean mControllable;
@@ -342,15 +341,6 @@ class InsetsSourceProvider {
mIsLeashReadyForDispatching = false;
final SurfaceControl leash = mAdapter.mCapturedLeash;
- final long frameNumber = mFinishSeamlessRotateFrameNumber;
- mFinishSeamlessRotateFrameNumber = -1;
- if (mWin.mHasSurface && leash != null) {
- // We just finished the seamless rotation. We don't want to change the position or the
- // window crop of the surface controls (including the leash) until the client finishes
- // drawing the new frame of the new orientation. Although we cannot defer the reparent
- // operation, it is fine, because reparent won't cause any visual effect.
- deferTransactionUntil(t, leash, frameNumber);
- }
mControlTarget = target;
updateVisibility();
mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition);
@@ -359,19 +349,14 @@ class InsetsSourceProvider {
}
void startSeamlessRotation() {
- if (!mSeamlessRotating) {
- mSeamlessRotating = true;
-
- // This will revoke the leash and clear the control target.
- mWin.cancelAnimation();
- }
+ if (!mSeamlessRotating) {
+ mSeamlessRotating = true;
+ mWin.cancelAnimation();
+ }
}
- void finishSeamlessRotation(boolean timeout) {
- if (mSeamlessRotating) {
- mSeamlessRotating = false;
- mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber();
- }
+ void finishSeamlessRotation() {
+ mSeamlessRotating = false;
}
boolean updateClientVisibility(InsetsControlTarget caller) {
@@ -529,7 +514,6 @@ class InsetsSourceProvider {
proto.write(CLIENT_VISIBLE, mClientVisible);
proto.write(SERVER_VISIBLE, mServerVisible);
proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
- proto.write(FINISH_SEAMLESS_ROTATE_FRAME_NUMBER, mFinishSeamlessRotateFrameNumber);
proto.write(CONTROLLABLE, mControllable);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 5d6d51377c3f..4ab5cd6a8d23 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -26,6 +32,7 @@ import android.os.SystemClock;
import android.util.proto.ProtoOutputStream;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -35,9 +42,11 @@ import java.util.ArrayList;
class NonAppWindowAnimationAdapter implements AnimationAdapter {
- private final WindowState mWindow;
+ private final WindowContainer mWindowContainer;
private RemoteAnimationTarget mTarget;
private SurfaceControl mCapturedLeash;
+ private SurfaceAnimator.OnAnimationFinishedCallback mCapturedLeashFinishCallback;
+ private @SurfaceAnimator.AnimationType int mLastAnimationType;
private long mDurationHint;
private long mStatusBarTransitionDelay;
@@ -47,21 +56,46 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
return false;
}
- NonAppWindowAnimationAdapter(WindowState w,
- long durationHint, long statusBarTransitionDelay) {
- mWindow = w;
+ NonAppWindowAnimationAdapter(WindowContainer w, long durationHint,
+ long statusBarTransitionDelay) {
+ mWindowContainer = w;
mDurationHint = durationHint;
mStatusBarTransitionDelay = statusBarTransitionDelay;
}
+ static RemoteAnimationTarget[] startNonAppWindowAnimations(WindowManagerService service,
+ DisplayContent displayContent, @WindowManager.TransitionOldType int transit,
+ long durationHint, long statusBarTransitionDelay,
+ ArrayList<NonAppWindowAnimationAdapter> adaptersOut) {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+ startNonAppWindowAnimationsForKeyguardExit(
+ service, durationHint, statusBarTransitionDelay, targets, adaptersOut);
+ } else if (transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT
+ || transit == TRANSIT_OLD_WALLPAPER_CLOSE) {
+ final boolean shouldAttachNavBarToApp =
+ displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+ && service.getRecentsAnimationController() == null
+ && displayContent.getFixedRotationAnimationController() == null;
+ if (shouldAttachNavBarToApp) {
+ startNavigationBarWindowAnimation(
+ displayContent, durationHint, statusBarTransitionDelay, targets,
+ adaptersOut);
+ }
+ }
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
/**
* Creates and starts remote animations for all the visible non app windows.
*
* @return RemoteAnimationTarget[] targets for all the visible non app windows
*/
- public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit(
- WindowManagerService service, long durationHint, long statusBarTransitionDelay) {
- final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ private static void startNonAppWindowAnimationsForKeyguardExit(WindowManagerService service,
+ long durationHint, long statusBarTransitionDelay,
+ ArrayList<RemoteAnimationTarget> targets,
+ ArrayList<NonAppWindowAnimationAdapter> adaptersOut) {
final WindowManagerPolicy policy = service.mPolicy;
service.mRoot.forAllWindows(nonAppWindow -> {
@@ -69,12 +103,30 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
&& nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) {
final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
nonAppWindow, durationHint, statusBarTransitionDelay);
+ adaptersOut.add(nonAppAdapter);
nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(),
nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
targets.add(nonAppAdapter.createRemoteAnimationTarget());
}
}, true /* traverseTopToBottom */);
- return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Creates and starts remote animation for the navigation bar windows.
+ *
+ * @return RemoteAnimationTarget[] targets for all the visible non app windows
+ */
+ private static void startNavigationBarWindowAnimation(DisplayContent displayContent,
+ long durationHint, long statusBarTransitionDelay,
+ ArrayList<RemoteAnimationTarget> targets,
+ ArrayList<NonAppWindowAnimationAdapter> adaptersOut) {
+ final WindowState navWindow = displayContent.getDisplayPolicy().getNavigationBar();
+ final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
+ navWindow.mToken, durationHint, statusBarTransitionDelay);
+ adaptersOut.add(nonAppAdapter);
+ navWindow.mToken.startAnimation(navWindow.mToken.getPendingTransaction(),
+ nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
+ targets.add(nonAppAdapter.createRemoteAnimationTarget());
}
/**
@@ -82,16 +134,39 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
*/
RemoteAnimationTarget createRemoteAnimationTarget() {
mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false,
- new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(),
- mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null,
- null);
+ new Rect(), null, mWindowContainer.getPrefixOrderIndex(),
+ mWindowContainer.getLastSurfacePosition(), mWindowContainer.getBounds(), null,
+ mWindowContainer.getWindowConfiguration(), true, null, null, null,
+ mWindowContainer.getWindowType());
return mTarget;
}
@Override
public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
mCapturedLeash = animationLeash;
+ mCapturedLeashFinishCallback = finishCallback;
+ mLastAnimationType = type;
+ }
+
+ /**
+ * @return the callback to call to clean up when the animation has finished.
+ */
+ SurfaceAnimator.OnAnimationFinishedCallback getLeashFinishedCallback() {
+ return mCapturedLeashFinishCallback;
+ }
+
+ /**
+ * @return the type of animation.
+ */
+ @SurfaceAnimator.AnimationType
+ int getLastAnimationType() {
+ return mLastAnimationType;
+ }
+
+ WindowContainer getWindowContainer() {
+ return mWindowContainer;
}
@Override
@@ -120,8 +195,8 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
@Override
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix);
- pw.print("token=");
- pw.println(mWindow.mToken);
+ pw.print("windowContainer=");
+ pw.println(mWindowContainer);
if (mTarget != null) {
pw.print(prefix);
pw.println("Target:");
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e02cce4b946a..64ff1084a6b8 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -17,7 +17,13 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -104,7 +110,8 @@ public class RecentsAnimationController implements DeathRecipient {
public @interface ReorderMode {}
private final WindowManagerService mService;
- private final StatusBarManagerInternal mStatusBar;
+ @VisibleForTesting
+ final StatusBarManagerInternal mStatusBar;
private IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
@@ -149,6 +156,7 @@ public class RecentsAnimationController implements DeathRecipient {
@VisibleForTesting
boolean mShouldAttachNavBarToAppDuringTransition;
+ private boolean mNavigationBarAttachedToApp;
/**
* Animates the screenshot of task that used to be controlled by RecentsAnimation.
@@ -225,7 +233,8 @@ public class RecentsAnimationController implements DeathRecipient {
}
@Override
- public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
+ float[] float9) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds);
final long token = Binder.clearCallingIdentity();
@@ -235,6 +244,8 @@ public class RecentsAnimationController implements DeathRecipient {
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
if (taskAdapter.mTask.mTaskId == taskId) {
taskAdapter.mFinishBounds.set(destinationBounds);
+ taskAdapter.mFinishWindowCrop.set(windowCrop);
+ taskAdapter.mFinishTransform = float9;
break;
}
}
@@ -369,7 +380,17 @@ public class RecentsAnimationController implements DeathRecipient {
}
@Override
- public void detachNavigationBarFromApp() {}
+ public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService.getWindowManagerLock()) {
+ restoreNavigationBarFromApp(moveHomeToTop);
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
/**
@@ -440,9 +461,7 @@ public class RecentsAnimationController implements DeathRecipient {
return;
}
- if (mShouldAttachNavBarToAppDuringTransition) {
- attachNavBarToApp();
- }
+ attachNavigationBarToApp();
// Adjust the wallpaper visibility for the showing target activity
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
@@ -577,32 +596,52 @@ public class RecentsAnimationController implements DeathRecipient {
}
}
+ boolean isNavigationBarAttachedToApp() {
+ return mNavigationBarAttachedToApp;
+ }
+
@VisibleForTesting
- WindowToken getNavigationBarWindowToken() {
- WindowState navBar = mDisplayContent.getDisplayPolicy().getNavigationBar();
- if (navBar != null) {
- return navBar.mToken;
- }
- return null;
+ WindowState getNavigationBarWindow() {
+ return mDisplayContent.getDisplayPolicy().getNavigationBar();
}
- private void attachNavBarToApp() {
+ private void attachNavigationBarToApp() {
+ if (!mShouldAttachNavBarToAppDuringTransition
+ // Skip the case where the nav bar is controlled by fixed rotation.
+ || mDisplayContent.getFixedRotationAnimationController() != null) {
+ return;
+ }
ActivityRecord topActivity = null;
+ boolean shouldTranslateNavBar = false;
+ final boolean isDisplayLandscape =
+ mDisplayContent.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
final Task task = adapter.mTask;
- if (!task.isHomeOrRecentsRootTask()) {
- topActivity = task.getTopVisibleActivity();
- break;
+ final boolean isSplitScreenSecondary =
+ task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ if (task.isHomeOrRecentsRootTask()
+ // TODO(b/178449492): Will need to update for the new split screen mode once
+ // it's ready.
+ // Skip if the task is the secondary split screen and in landscape.
+ || (isSplitScreenSecondary && isDisplayLandscape)) {
+ continue;
}
+ shouldTranslateNavBar = isSplitScreenSecondary;
+ topActivity = task.getTopVisibleActivity();
+ break;
}
- final WindowToken navToken = getNavigationBarWindowToken();
- if (topActivity == null || navToken == null) {
+
+ final WindowState navWindow = getNavigationBarWindow();
+ if (topActivity == null || navWindow == null || navWindow.mToken == null) {
return;
}
-
- final SurfaceControl.Transaction t = navToken.getPendingTransaction();
- final SurfaceControl navSurfaceControl = navToken.getSurfaceControl();
+ mNavigationBarAttachedToApp = true;
+ final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+ final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+ if (shouldTranslateNavBar) {
+ navWindow.setSurfaceTranslationY(-topActivity.getBounds().top);
+ }
t.reparent(navSurfaceControl, topActivity.getSurfaceControl());
t.show(navSurfaceControl);
@@ -613,16 +652,32 @@ public class RecentsAnimationController implements DeathRecipient {
// Place the nav bar on top of anything else in the top activity.
t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
}
+ if (mStatusBar != null) {
+ mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, false);
+ }
}
- private void restoreNavBarFromApp(boolean animate) {
- // Reparent the SurfaceControl of nav bar token back.
- final WindowToken navToken = getNavigationBarWindowToken();
- final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
- if (navToken != null) {
- final WindowContainer parent = navToken.getParent();
- t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+ private void restoreNavigationBarFromApp(boolean animate) {
+ if (!mNavigationBarAttachedToApp) {
+ return;
+ }
+ if (mStatusBar != null) {
+ mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, true);
+ }
+
+ final WindowState navWindow = getNavigationBarWindow();
+ if (navWindow == null) {
+ return;
}
+ navWindow.setSurfaceTranslationY(0);
+
+ if (navWindow.mToken == null) {
+ return;
+ }
+ final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
+ final WindowContainer parent = navWindow.mToken.getParent();
+ // Reparent the SurfaceControl of nav bar token back.
+ t.reparent(navWindow.mToken.getSurfaceControl(), parent.getSurfaceControl());
if (animate) {
// Run fade-in animation to show navigation bar back to bottom of the display.
@@ -852,9 +907,7 @@ public class RecentsAnimationController implements DeathRecipient {
removeWallpaperAnimation(wallpaperAdapter);
}
- if (mShouldAttachNavBarToAppDuringTransition) {
- restoreNavBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
- }
+ restoreNavigationBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
// Clear any pending failsafe runnables
mService.mH.removeCallbacks(mFailsafeRunnable);
@@ -1038,6 +1091,9 @@ public class RecentsAnimationController implements DeathRecipient {
private final Rect mLocalBounds = new Rect();
// The bounds of the target when animation is finished
private final Rect mFinishBounds = new Rect();
+ // Bounds and transform for the final transaction.
+ private final Rect mFinishWindowCrop = new Rect();
+ private float[] mFinishTransform;
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
@@ -1074,13 +1130,31 @@ public class RecentsAnimationController implements DeathRecipient {
void onCleanup() {
if (!mFinishBounds.isEmpty()) {
- // Apply any pending bounds changes
- final SurfaceControl taskSurface = mTask.getSurfaceControl();
- mTask.getPendingTransaction()
- .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top)
- .setWindowCrop(taskSurface, mFinishBounds.width(), mFinishBounds.height())
+ final SurfaceControl taskSurface = mTask.mSurfaceControl;
+ final Transaction pendingTransaction = mTask.getPendingTransaction();
+ if (mFinishTransform != null) {
+ pendingTransaction
+ .setMatrix(taskSurface,
+ mFinishTransform[MSCALE_X], mFinishTransform[MSKEW_Y],
+ mFinishTransform[MSKEW_X], mFinishTransform[MSCALE_Y]);
+ }
+ float left = mFinishBounds.left;
+ float top = mFinishBounds.top;
+ if (!mFinishWindowCrop.isEmpty()) {
+ pendingTransaction.setWindowCrop(taskSurface, mFinishWindowCrop);
+ if (mFinishTransform != null) {
+ // adjust the position for insets.
+ left -= mFinishWindowCrop.left * mFinishTransform[MSCALE_X];
+ top -= mFinishWindowCrop.top * mFinishTransform[MSCALE_Y];
+ }
+ }
+ pendingTransaction
+ .setPosition(taskSurface, left, top)
.apply();
mTask.mLastRecentsAnimationBounds.set(mFinishBounds);
+ // reset the variables
+ mFinishTransform = null;
+ mFinishWindowCrop.setEmpty();
mFinishBounds.setEmpty();
} else if (!mTask.isAttached()) {
// Apply the task's pending transaction in case it is detached and its transaction
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 42cb96f65738..f851e3559def 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -41,6 +38,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FastPrintWriter;
@@ -60,10 +58,13 @@ class RemoteAnimationController implements DeathRecipient {
private static final long TIMEOUT_MS = 2000;
private final WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
private final RemoteAnimationAdapter mRemoteAnimationAdapter;
private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
new ArrayList<>();
+ @VisibleForTesting
+ final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
@@ -72,9 +73,10 @@ class RemoteAnimationController implements DeathRecipient {
private boolean mCanceled;
private boolean mLinkedToDeathOfRunner;
- RemoteAnimationController(WindowManagerService service,
+ RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
mService = service;
+ mDisplayContent = displayContent;
mRemoteAnimationAdapter = remoteAnimationAdapter;
mHandler = handler;
}
@@ -104,11 +106,11 @@ class RemoteAnimationController implements DeathRecipient {
*/
void goodToGo(@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
- if (mPendingAnimations.isEmpty() || mCanceled) {
+ if (mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
- "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
- mCanceled, mPendingAnimations.size());
+ "goodToGo(): Animation canceled already");
onAnimationFinished();
+ invokeAnimationCancelled();
return;
}
@@ -120,8 +122,11 @@ class RemoteAnimationController implements DeathRecipient {
// Create the app targets
final RemoteAnimationTarget[] appTargets = createAppAnimations();
if (appTargets.length == 0) {
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
+ "goodToGo(): No apps to animate, mPendingAnimations=%d",
+ mPendingAnimations.size());
onAnimationFinished();
+ invokeAnimationCancelled();
return;
}
@@ -221,12 +226,12 @@ class RemoteAnimationController implements DeathRecipient {
private RemoteAnimationTarget[] createNonAppWindowAnimations(
@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()");
- return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
- || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
- ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService,
- mRemoteAnimationAdapter.getDuration(),
- mRemoteAnimationAdapter.getStatusBarTransitionDelay())
- : new RemoteAnimationTarget[0];
+ return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService,
+ mDisplayContent,
+ transit,
+ mRemoteAnimationAdapter.getDuration(),
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
+ mPendingNonAppAnimations);
}
private void onAnimationFinished() {
@@ -264,6 +269,15 @@ class RemoteAnimationController implements DeathRecipient {
mPendingWallpaperAnimations.remove(i);
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
}
+
+ for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) {
+ final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i);
+ adapter.getLeashFinishedCallback().onAnimationFinished(
+ adapter.getLastAnimationType(), adapter);
+ mPendingNonAppAnimations.remove(i);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s",
+ adapter.getWindowContainer());
+ }
} catch (Exception e) {
Slog.e(TAG, "Failed to finish remote animation", e);
throw e;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 1212302d40a5..422d4e79e259 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -273,7 +273,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
private int mTmpTaskLayerRank;
- private final LockedScheduler mRankTaskLayersScheduler;
+ private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable();
private boolean mTmpBoolean;
private RemoteException mTmpRemoteException;
@@ -451,12 +451,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
- mRankTaskLayersScheduler = new LockedScheduler(mService) {
- @Override
- public void execute() {
- rankTaskLayersIfNeeded();
- }
- };
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -2660,16 +2654,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void invalidateTaskLayers() {
- mTaskLayersChanged = true;
- mRankTaskLayersScheduler.scheduleIfNeeded();
+ if (!mTaskLayersChanged) {
+ mTaskLayersChanged = true;
+ mService.mH.post(mRankTaskLayersRunnable);
+ }
}
/** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
- void rankTaskLayersIfNeeded() {
- if (!mTaskLayersChanged) {
- return;
+ void rankTaskLayers() {
+ if (mTaskLayersChanged) {
+ mTaskLayersChanged = false;
+ mService.mH.removeCallbacks(mRankTaskLayersRunnable);
}
- mTaskLayersChanged = false;
mTmpTaskLayerRank = 0;
// Only rank for leaf tasks because the score of activity is based on immediate parent.
forAllLeafTasks(task -> {
@@ -2818,7 +2814,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @param launchParams The resolved launch params to use.
* @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
* @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
- * @return The roott task to use for the launch or INVALID_TASK_ID.
+ * @return The root task to use for the launch or INVALID_TASK_ID.
*/
Task getLaunchRootTask(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop,
@@ -2887,7 +2883,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Falling back to default task container
taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea();
rootTask = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
- activityType, onTop);
+ launchParams, activityType, onTop);
if (rootTask != null) {
return rootTask;
}
@@ -2942,7 +2938,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- return container.getOrCreateRootTask(r, options, candidateTask, activityType, onTop);
+ return container.getOrCreateRootTask(
+ r, options, candidateTask, launchParams, activityType, onTop);
}
/** @return true if activity record is null or can be launched on provided display. */
@@ -3668,32 +3665,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- /**
- * Helper class to schedule the runnable if it hasn't scheduled on display thread inside window
- * manager lock.
- */
- abstract static class LockedScheduler implements Runnable {
- private final ActivityTaskManagerService mService;
- private boolean mScheduled;
-
- LockedScheduler(ActivityTaskManagerService service) {
- mService = service;
- }
-
+ private class RankTaskLayersRunnable implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
- mScheduled = false;
- execute();
- }
- }
-
- abstract void execute();
-
- void scheduleIfNeeded() {
- if (!mScheduled) {
- mService.mH.post(this);
- mScheduled = true;
+ if (mTaskLayersChanged) {
+ mTaskLayersChanged = false;
+ rankTaskLayers();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 3d305e4d852d..4cc369f0a187 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -35,9 +35,6 @@ import java.io.StringWriter;
* Helper class for seamless rotation.
*
* Works by transforming the {@link WindowState} back into the old display rotation.
- *
- * Uses {@link Transaction#deferTransactionUntil(SurfaceControl, IBinder, long)} instead of
- * latching on the buffer size to allow for seamless 180 degree rotations.
*/
public class SeamlessRotator {
@@ -103,23 +100,12 @@ public class SeamlessRotator {
* Removing the transform and the result of the {@link WindowState} layout are both tied to the
* {@link WindowState} next frame, such that they apply at the same time the client draws the
* window in the new orientation.
- *
- * In the case of a rotation timeout, we want to remove the transform immediately and not defer
- * it.
*/
- public void finish(WindowState win, boolean timeout) {
- final Transaction t = win.getPendingTransaction();
- finish(t, win);
- if (win.mWinAnimator.mSurfaceController != null && !timeout) {
- t.deferTransactionUntil(win.mSurfaceControl,
- win.getClientViewRootSurface(), win.getFrameNumber());
- t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
- win.getClientViewRootSurface(), win.getFrameNumber());
+ void finish(Transaction t, WindowContainer win) {
+ if (win.mSurfaceControl == null || !win.mSurfaceControl.isValid()) {
+ return;
}
- }
- /** Removes the transform and restore to the original last position. */
- void finish(Transaction t, WindowContainer win) {
mTransform.reset();
t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index ef4a40f4837c..070856949bb7 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -40,9 +40,8 @@ public class StartingSurfaceController {
private static final String TAG = TAG_WITH_CLASS_NAME
? StartingSurfaceController.class.getSimpleName() : TAG_WM;
/** Set to {@code true} to enable shell starting surface drawer. */
- private static final boolean DEBUG_ENABLE_SHELL_DRAWER =
- SystemProperties.getBoolean("persist.debug.shell_starting_surface", false);
-
+ static final boolean DEBUG_ENABLE_SHELL_DRAWER =
+ SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
private final WindowManagerService mService;
public StartingSurfaceController(WindowManagerService wm) {
@@ -53,8 +52,8 @@ public class StartingSurfaceController {
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
if (!DEBUG_ENABLE_SHELL_DRAWER) {
- return mService.mPolicy.addSplashScreen(activity.token, packageName, theme,
- compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
overrideConfig, displayId);
}
@@ -139,8 +138,9 @@ public class StartingSurfaceController {
}
@Override
- public void remove() {
- mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask);
+ public void remove(boolean animate) {
+ mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
+ animate);
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d992a4591a22..09a8e4f23644 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2382,11 +2382,11 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
- void migrateToNewSurfaceControl() {
- super.migrateToNewSurfaceControl();
+ void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
+ super.migrateToNewSurfaceControl(t);
mLastSurfaceSize.x = 0;
mLastSurfaceSize.y = 0;
- updateSurfaceSize(getPendingTransaction());
+ updateSurfaceSize(t);
}
void updateSurfaceSize(SurfaceControl.Transaction transaction) {
@@ -4135,6 +4135,8 @@ class Task extends WindowContainer<WindowContainer> {
final StartingWindowInfo info = new StartingWindowInfo();
info.taskInfo = getTaskInfo();
+ info.isKeyguardOccluded =
+ mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
final ActivityRecord topActivity = getTopMostActivity();
if (topActivity != null) {
info.startingWindowTypeParameter =
@@ -4909,9 +4911,15 @@ class Task extends WindowContainer<WindowContainer> {
task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
task.setBounds(lastNonFullscreenBounds);
task.mWindowLayoutAffinity = windowLayoutAffinity;
+ if (activities.size() > 0) {
+ // We need to add the task into hierarchy before adding child to it.
+ final DisplayContent dc =
+ taskSupervisor.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+ dc.getDefaultTaskDisplayArea().addChild(task, POSITION_BOTTOM);
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- task.addChild(activities.get(activityNdx));
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ task.addChild(activities.get(activityNdx));
+ }
}
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
@@ -5245,17 +5253,29 @@ class Task extends WindowContainer<WindowContainer> {
if (mForceHiddenFlags == newFlags) {
return false;
}
+
final boolean wasHidden = isForceHidden();
+ final boolean wasVisible = isVisible();
mForceHiddenFlags = newFlags;
- if (wasHidden != isForceHidden() && isTopActivityFocusable()) {
- // The change in force-hidden state will change visibility without triggering a root
- // task order change, so we should reset the preferred top focusable root task to ensure
- // it's not used if a new activity is started from this task.
- getDisplayArea().resetPreferredTopFocusableRootTaskIfNeeded(this);
+ final boolean nowHidden = isForceHidden();
+ if (wasHidden != nowHidden) {
+ final String reason = "setForceHidden";
+ if (wasVisible && nowHidden) {
+ // Move this visible task to back when the task is forced hidden
+ moveToBack(reason, null);
+ } else if (isAlwaysOnTop()) {
+ // Move this always-on-top task to front when no longer hidden
+ moveToFront(reason);
+ }
}
return true;
}
+ @Override
+ public boolean isAlwaysOnTop() {
+ return !isForceHidden() && super.isAlwaysOnTop();
+ }
+
/**
* Returns whether this task is currently forced to be hidden for any reason.
*/
@@ -7482,17 +7502,22 @@ class Task extends WindowContainer<WindowContainer> {
}
public void setAlwaysOnTop(boolean alwaysOnTop) {
- if (isAlwaysOnTop() == alwaysOnTop) {
+ // {@link #isAwaysonTop} overrides the original behavior which also evaluates if this
+ // task is force hidden, so super.isAlwaysOnTop() is used here to see whether the
+ // alwaysOnTop attributes should be updated.
+ if (super.isAlwaysOnTop() == alwaysOnTop) {
return;
}
super.setAlwaysOnTop(alwaysOnTop);
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
// positionChildAtTop() must be called even when always on top gets turned off because we
// need to make sure that the root task is moved from among always on top windows to
// below other always on top windows. Since the position the root task should be inserted
// into is calculated properly in {@link DisplayContent#getTopInsertPosition()} in both
// cases, we can just request that the root task is put at top here.
- taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ // Don't bother moving task to top if this task is force hidden and invisible to user.
+ if (!isForceHidden()) {
+ getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ }
}
void dismissPip() {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ed92fd08bef5..88e9ae9179c9 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -57,6 +57,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.wm.LaunchParamsController.LaunchParams;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -393,7 +394,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
final boolean moveToBottom = position <= 0;
final int oldPosition = mChildren.indexOf(child);
- if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
+ if (child.isAlwaysOnTop() && !moveToTop) {
// This root task is always-on-top, override the default behavior.
Slog.w(TAG_WM, "Ignoring move of always-on-top root task=" + this + " to bottom");
@@ -962,6 +963,22 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
}
+ @Override
+ void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
+ super.migrateToNewSurfaceControl(t);
+ if (mAppAnimationLayer == null) {
+ return;
+ }
+
+ // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
+ t.reparent(mAppAnimationLayer, mSurfaceControl);
+ t.reparent(mBoostedAppAnimationLayer, mSurfaceControl);
+ t.reparent(mHomeAppAnimationLayer, mSurfaceControl);
+ t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
+ reassignLayer(t);
+ scheduleAnimation();
+ }
+
void onRootTaskRemoved(Task rootTask) {
if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
Slog.v(TAG_ROOT_TASK, "onRootTaskRemoved: detaching " + rootTask + " from displayId="
@@ -974,14 +991,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
onRootTaskOrderChanged(rootTask);
}
- /** Reset the mPreferredTopFocusableRootTask if it is or below the given task. */
- void resetPreferredTopFocusableRootTaskIfNeeded(Task task) {
- if (mPreferredTopFocusableRootTask != null
- && mPreferredTopFocusableRootTask.compareTo(task) <= 0) {
- mPreferredTopFocusableRootTask = null;
- }
- }
-
/**
* Moves/reparents `task` to the back of whatever container the root home task is in. This is
* for when we just want to move a task to "the back" vs. a specific place. The primary use-case
@@ -1080,11 +1089,17 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
* @see #getOrCreateRootTask(int, int, boolean)
*/
Task getOrCreateRootTask(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
- boolean onTop) {
- // First preference is the windowing mode in the activity options if set.
- int windowingMode = (options != null)
- ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ @Nullable ActivityOptions options, @Nullable Task candidateTask,
+ @Nullable LaunchParams launchParams, int activityType, boolean onTop) {
+ int windowingMode = WINDOWING_MODE_UNDEFINED;
+ if (launchParams != null) {
+ // If launchParams isn't null, windowing mode is already resolved.
+ windowingMode = launchParams.mWindowingMode;
+ } else if (options != null) {
+ // If launchParams is null and options isn't let's use the windowing mode in the
+ // options.
+ windowingMode = options.getLaunchWindowingMode();
+ }
// Validate that our desired windowingMode will work under the current conditions.
// UNDEFINED windowing mode is a valid result and means that the new root task will inherit
// it's display's windowing mode.
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fc6db61bdbcd..375b3f49be13 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -32,6 +32,7 @@ import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -131,10 +132,28 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
});
}
- void removeStartingWindow(Task task) {
+ void removeStartingWindow(Task task, boolean prepareAnimation) {
mDeferTaskOrgCallbacksConsumer.accept(() -> {
+ SurfaceControl firstWindowLeash = null;
+ Rect mainFrame = null;
+ // TODO enable shift up animation once we fix flicker test
+// final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+// if (prepareAnimation && playShiftUpAnimation) {
+// final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+// if (topActivity != null) {
+// final WindowState mainWindow =
+// topActivity.findMainWindow(false/* includeStartingApp */);
+// if (mainWindow != null) {
+ // TODO create proper leash instead of the copied SC
+// firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
+// "TaskOrganizerController.removeStartingWindow");
+// mainFrame = mainWindow.getRelativeFrame();
+// }
+// }
+// }
try {
- mTaskOrganizer.removeStartingWindow(task.mTaskId);
+ mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
+ prepareAnimation);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
}
@@ -249,8 +268,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mOrganizer.addStartingWindow(t, appToken, launchTheme);
}
- void removeStartingWindow(Task t) {
- mOrganizer.removeStartingWindow(t);
+ void removeStartingWindow(Task t, boolean prepareAnimation) {
+ mOrganizer.removeStartingWindow(t, prepareAnimation);
}
void copySplashScreenView(Task t) {
@@ -291,7 +310,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
boolean taskAppearedSent = t.mTaskAppearedSent;
if (taskAppearedSent) {
if (t.getSurfaceControl() != null) {
- t.migrateToNewSurfaceControl();
+ t.migrateToNewSurfaceControl(t.getSyncTransaction());
}
t.mTaskAppearedSent = false;
}
@@ -495,14 +514,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return true;
}
- void removeStartingWindow(Task task) {
+ void removeStartingWindow(Task task, boolean prepareAnimation) {
final Task rootTask = task.getRootTask();
if (rootTask == null || rootTask.mTaskOrganizer == null) {
return;
}
final TaskOrganizerState state =
mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.removeStartingWindow(task);
+ state.removeStartingWindow(task, prepareAnimation);
}
boolean copySplashScreenView(Task task) {
@@ -865,6 +884,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mPendingTaskEvents.remove(pending);
}
mPendingTaskEvents.add(pending);
+ mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 855dd7e416b7..b8d2febd5dd4 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -328,10 +328,19 @@ public class TaskPersister implements PersisterQueue.Listener {
// mWriteQueue.add(new TaskWriteQueueItem(task));
final int taskId = task.mTaskId;
- if (mService.mRootWindowContainer.anyTaskForId(taskId,
+ final boolean persistedTask = task.hasActivity();
+ if (persistedTask && mRecentTasks.getTask(taskId) != null) {
+ // The persisted task is added into hierarchy and will also be
+ // added to recent tasks later. So this task should not exist
+ // in recent tasks before it is added.
+ Slog.wtf(TAG, "Existing persisted task with taskId " + taskId
+ + " found");
+ } else if (!persistedTask
+ && mService.mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS) != null) {
// Should not happen.
- Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
+ Slog.wtf(TAG, "Existing task with taskId " + taskId
+ + " found");
} else if (userId != task.mUserId) {
// Should not happen.
Slog.wtf(TAG, "Task with userId " + task.mUserId + " found in "
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 9d35c25fc546..525420e045b5 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -188,7 +188,8 @@ class TaskPositioningController {
transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel)) {
+ transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
+ false /* isDragDrop */)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b810de99ee10..8915eba3d509 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -37,6 +37,7 @@ import android.os.Handler;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
+import android.view.Display;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
@@ -624,7 +625,7 @@ class TaskSnapshotController {
/**
* Called when screen is being turned off.
*/
- void screenTurningOff(ScreenOffListener listener) {
+ void screenTurningOff(int displayId, ScreenOffListener listener) {
if (shouldDisableSnapshots()) {
listener.onScreenOff();
return;
@@ -635,7 +636,7 @@ class TaskSnapshotController {
try {
synchronized (mService.mGlobalLock) {
mTmpTasks.clear();
- mService.mRoot.forAllTasks(task -> {
+ mService.mRoot.getDisplayContent(displayId).forAllTasks(task -> {
// Since RecentsAnimation will handle task snapshot while switching apps
// with the best capture timing (e.g. IME window capture), No need
// additional task capture while task is controlled by RecentsAnimation.
@@ -645,7 +646,7 @@ class TaskSnapshotController {
});
// Allow taking snapshot of home when turning screen off to reduce the delay of
// waking from secure lock to home.
- final boolean allowSnapshotHome =
+ final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY &&
mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId);
snapshotTasks(mTmpTasks, allowSnapshotHome);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 07610ab6d546..79a6bd7dcd2c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -281,13 +281,14 @@ class TaskSnapshotSurface implements StartingSurface {
}
@Override
- public void remove() {
+ public void remove(boolean animate) {
synchronized (mService.mGlobalLock) {
final long now = SystemClock.uptimeMillis();
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
// Show the latest content as soon as possible for unlocking to home.
&& mActivityType != ACTIVITY_TYPE_HOME) {
- mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
+ mHandler.postAtTime(() -> remove(false /* prepareAnimation */),
+ mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"Defer removing snapshot surface in %dms", (now - mShownTime));
@@ -517,7 +518,7 @@ class TaskSnapshotSurface implements StartingSurface {
// The orientation of the screen is changing. We better remove the snapshot ASAP as
// we are going to wait on the new window in any case to unfreeze the screen, and
// the starting window is not needed anymore.
- sHandler.post(mOuter::remove);
+ sHandler.post(() -> mOuter.remove(false /* prepareAnimation */));
}
if (reportDraw) {
sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8d859584d5f5..0bb9854d8020 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -29,6 +29,7 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -389,12 +390,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mParent.onChildAdded(this);
}
if (!mReparenting) {
+ onSyncReparent(oldParent, mParent);
if (mParent != null && mParent.mDisplayContent != null
&& mDisplayContent != mParent.mDisplayContent) {
onDisplayChanged(mParent.mDisplayContent);
}
onParentChanged(mParent, oldParent);
- onSyncReparent(oldParent, mParent);
}
}
@@ -460,8 +461,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* This is used to revoke control of the SurfaceControl from a client process that was
* previously organizing this WindowContainer.
*/
- void migrateToNewSurfaceControl() {
- SurfaceControl.Transaction t = getPendingTransaction();
+ void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
t.remove(mSurfaceControl);
// Clear the last position so the new SurfaceControl will get correct position
mLastSurfacePosition.set(0, 0);
@@ -3309,4 +3309,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mListeners.remove(listener);
unregisterConfigurationChangeListener(listener);
}
+
+ /**
+ * Returns the {@link WindowManager.LayoutParams.WindowType}.
+ */
+ @WindowManager.LayoutParams.WindowType int getWindowType() {
+ return INVALID_WINDOW_TYPE;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 7450782364f4..8148f15981b3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -221,7 +221,8 @@ public abstract class WindowManagerInternal {
DragState state, Display display, InputManagerService service,
InputChannel source) {
state.register(display);
- return service.transferTouchFocus(source, state.getInputChannel());
+ return service.transferTouchFocus(source, state.getInputChannel(),
+ true /* isDragDrop */);
}
/**
@@ -450,24 +451,15 @@ public abstract class WindowManagerInternal {
public abstract int getInputMethodWindowVisibleHeight(int displayId);
/**
- * Notifies WindowManagerService that the current IME window status is being changed.
+ * Notifies WindowManagerService that the expected back-button behavior might have changed.
*
* <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
* tested caller of this method.</p>
*
- * @param imeToken token to track the active input method. Corresponding IME windows can be
- * identified by checking {@link android.view.WindowManager.LayoutParams#token}.
- * Note that there is no guarantee that the corresponding window is already
- * created
- * @param imeWindowVisible whether the active IME thinks that its window should be visible or
- * hidden, no matter how WindowManagerService will react / has reacted
- * to corresponding API calls. Note that this state is not guaranteed
- * to be synchronized with state in WindowManagerService.
* @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
* key is expected to dismiss the software keyboard.
*/
- public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed);
+ public abstract void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed);
/**
* Notifies WindowManagerService that the current IME window status is being changed.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6f853c795525..c5e000000eee 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -84,8 +84,6 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
-import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
-import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -233,7 +231,7 @@ import android.view.IOnKeyguardExitResult;
import android.view.IPinnedTaskListener;
import android.view.IRecentsAnimationRunner;
import android.view.IRotationWatcher;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
@@ -273,7 +271,6 @@ import android.window.TaskSnapshot;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
@@ -367,9 +364,6 @@ public class WindowManagerService extends IWindowManager.Stub
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
- /** Amount of time (in milliseconds) to delay before declaring a seamless rotation timeout. */
- static final int SEAMLESS_ROTATION_TIMEOUT_DURATION = 2000;
-
/** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
@@ -529,14 +523,6 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
- // Bugreport dumps the trace 2x, 1x as proto and 1x as text. Save file to disk only 1x.
- if (asProto && mWindowTracing.isEnabled()) {
- mWindowTracing.stopTrace(null, false /* writeToFile */);
- BackgroundThread.getHandler().post(() -> {
- mWindowTracing.writeTraceToFile();
- mWindowTracing.startTrace(null);
- });
- }
doDump(fd, pw, new String[] {"-a"}, asProto);
}
@@ -1614,11 +1600,17 @@ public class WindowManagerService extends IWindowManager.Stub
ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
+ ".%s Aborting.", token);
return WindowManagerGlobal.ADD_APP_EXITING;
- } else if (type == TYPE_APPLICATION_STARTING && activity.mStartingWindow != null) {
- ProtoLog.w(WM_ERROR,
- "Attempted to add starting window to token with already existing"
- + " starting window");
- return WindowManagerGlobal.ADD_DUPLICATE_ADD;
+ } else if (type == TYPE_APPLICATION_STARTING) {
+ if (activity.mStartingWindow != null) {
+ ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
+ + "token with already existing starting window");
+ return WindowManagerGlobal.ADD_DUPLICATE_ADD;
+ }
+ if (activity.mStartingData == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
+ + "token but already cleaned");
+ return WindowManagerGlobal.ADD_DUPLICATE_ADD;
+ }
}
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
@@ -2245,9 +2237,6 @@ public class WindowManagerService extends IWindowManager.Stub
win.setFrameNumber(frameNumber);
final DisplayContent dc = win.getDisplayContent();
- if (!dc.mWaitingForConfig) {
- win.finishSeamlessRotation(false /* timeout */);
- }
if (win.mPendingPositionChanged != null) {
win.mPendingPositionChanged.updateLeashPosition(frameNumber);
@@ -2255,6 +2244,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) {
+ win.prepareDrawHandlers();
result |= RELAYOUT_RES_BLAST_SYNC;
}
@@ -2999,8 +2989,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void screenTurningOff(ScreenOffListener listener) {
- mTaskSnapshotController.screenTurningOff(listener);
+ public void screenTurningOff(int displayId, ScreenOffListener listener) {
+ mTaskSnapshotController.screenTurningOff(displayId, listener);
}
@Override
@@ -5095,7 +5085,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_HIDE_TIMEOUT = 52;
- public static final int SEAMLESS_ROTATION_TIMEOUT = 54;
public static final int RESTORE_POINTER_ICON = 55;
public static final int SET_HAS_OVERLAY_UI = 58;
public static final int ANIMATION_FAILSAFE = 60;
@@ -5378,13 +5367,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
break;
}
- case SEAMLESS_ROTATION_TIMEOUT: {
- final DisplayContent displayContent = (DisplayContent) msg.obj;
- synchronized (mGlobalLock) {
- displayContent.getDisplayRotation().onSeamlessRotationTimeout();
- }
- break;
- }
case SET_HAS_OVERLAY_UI: {
mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
break;
@@ -5702,6 +5684,11 @@ public class WindowManagerService extends IWindowManager.Stub
mBlurController.unregisterCrossWindowBlurEnabledListener(listener);
}
+ @Override
+ public void setForceCrossWindowBlurDisabled(boolean disable) {
+ mBlurController.setForceCrossWindowBlurDisabled(disable);
+ }
+
// -------------------------------------------------------------
// Internals
// -------------------------------------------------------------
@@ -7164,10 +7151,10 @@ public class WindowManagerService extends IWindowManager.Stub
* @param displayId the display for the request
* @param behindClient token for a window, used to filter the search to windows behind it
* @param taskId specifies the id of a task the result must belong to or -1 to match any task
- * @param callbacks to receive responses
+ * @param listener to receive the response
*/
public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId,
- IScrollCaptureCallbacks callbacks) {
+ IScrollCaptureResponseListener listener) {
if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
@@ -7180,7 +7167,7 @@ public class WindowManagerService extends IWindowManager.Stub
ProtoLog.e(WM_ERROR,
"Invalid displayId for requestScrollCapture: %d", displayId);
responseBuilder.setDescription(String.format("bad displayId: %d", displayId));
- callbacks.onScrollCaptureResponse(responseBuilder.build());
+ listener.onScrollCaptureResponse(responseBuilder.build());
return;
}
WindowState topWindow = null;
@@ -7190,19 +7177,19 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId);
if (targetWindow == null) {
responseBuilder.setDescription("findScrollCaptureTargetWindow returned null");
- callbacks.onScrollCaptureResponse(responseBuilder.build());
+ listener.onScrollCaptureResponse(responseBuilder.build());
return;
}
try {
// Forward to the window for handling, which will respond using the callback.
- targetWindow.mClient.requestScrollCapture(callbacks);
+ targetWindow.mClient.requestScrollCapture(listener);
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR,
"requestScrollCapture: caught exception dispatching to window."
+ "token=%s", targetWindow.mClient.asBinder());
responseBuilder.setWindowTitle(targetWindow.getName());
responseBuilder.setDescription(String.format("caught exception: %s", e));
- callbacks.onScrollCaptureResponse(responseBuilder.build());
+ listener.onScrollCaptureResponse(responseBuilder.build());
}
}
} catch (RemoteException e) {
@@ -7737,8 +7724,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
+ public void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed) {
mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
}
@@ -8655,14 +8641,16 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Invalid window");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
@@ -8672,7 +8660,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (boundsInDisplay.isEmpty()) {
Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
}
@@ -8682,23 +8671,13 @@ public class WindowManagerService extends IWindowManager.Stub
// be covering it with the same uid. We want to make sure we include content that's
// covering to ensure we get as close as possible to what the user sees
final int uid = session.mUid;
- SurfaceControl.LayerCaptureArgs args =
+ SurfaceControl.LayerCaptureArgs.Builder args =
new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl)
.setUid(uid)
- .setSourceCrop(boundsInDisplay)
- .build();
-
- SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
- SurfaceControl.captureLayers(args);
- if (screenshotHardwareBuffer == null
- || screenshotHardwareBuffer.getHardwareBuffer() == null) {
- Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
- sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
- return;
- }
+ .setSourceCrop(boundsInDisplay);
- mDisplayHashController.generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(),
- boundsInWindow, hashAlgorithm, callback);
+ mDisplayHashController.generateDisplayHash(args, boundsInWindow,
+ hashAlgorithm, callback);
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
@@ -8716,10 +8695,4 @@ public class WindowManagerService extends IWindowManager.Stub
return snapshot != null && snapshot.hasImeSurface();
}
}
-
- private void sendDisplayHashError(RemoteCallback callback, int errorCode) {
- Bundle bundle = new Bundle();
- bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
- callback.sendResult(bundle);
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c5e24a9df3a7..bac1ab1264ab 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -225,6 +225,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19;
private static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20;
private static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21;
+ private static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22;
private static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
/**
@@ -479,7 +480,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
void setLastActivityFinishTimeIfNeeded(long finishTime) {
- if (finishTime <= mLastActivityFinishTime || !hasVisibleActivities()) {
+ if (finishTime <= mLastActivityFinishTime || !hasActivityInVisibleTask()) {
return;
}
mLastActivityFinishTime = finishTime;
@@ -516,7 +517,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
boolean isCheckingForFgsStart) {
return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
- appSwitchAllowed, isCheckingForFgsStart, hasVisibleActivities(),
+ appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(),
mInstrumentingWithBackgroundActivityStartPrivileges,
mAtm.getLastStopAppSwitchesTime(),
mLastActivityLaunchTime, mLastActivityFinishTime);
@@ -653,6 +654,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return (mActivityStateFlags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0;
}
+ boolean hasActivityInVisibleTask() {
+ return (mActivityStateFlags & ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK) != 0;
+ }
+
@HotPath(caller = HotPath.LRU_UPDATE)
public boolean hasActivitiesOrRecentTasks() {
return mHasActivities || mHasRecentTasks;
@@ -996,11 +1001,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (r.isVisible()) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
}
+ final Task task = r.getTask();
+ if (task != null && task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
+ stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
+ }
if (r.mVisibleRequested) {
if (r.isState(RESUMED)) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
}
- final Task task = r.getTask();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
@@ -1048,7 +1056,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Called when the process has some oom related changes and it is going to update oom-adj. */
private void prepareOomAdjustment() {
- mAtm.mRootWindowContainer.rankTaskLayersIfNeeded();
+ mAtm.mRootWindowContainer.rankTaskLayers();
mAtm.mTaskSupervisor.computeProcessActivityStateBatch();
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d9b879fdf8dc..6d88387fe25c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -168,8 +168,8 @@ import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_
import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE;
+import static com.android.server.wm.WindowStateProto.HAS_COMPAT_SCALE;
import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
-import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
@@ -261,6 +261,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/** A window in the window manager. */
@@ -749,6 +750,36 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private final WindowProcessController mWpcForDisplayAreaConfigChanges;
/**
+ * We split the draw handlers in to a "pending" and "ready" list, in order to solve
+ * sequencing problems. Think of it this way, let's say I update a windows orientation
+ * (in configuration), and then I call applyWithNextDraw. What I'm hoping for is to
+ * apply with the draw that contains the orientation change. However, since the client
+ * can call finishDrawing at any time, it could be about to call a previous call to
+ * finishDrawing (or maybe its already called it, we just haven't handled it). Since this
+ * frame was already completed it had no time to include the orientation change we made.
+ * To solve this problem we accumulate draw handlers in mPendingDrawHandlers, and then force
+ * the client to call relayout. Only the frame post relayout will contain the configuration
+ * change since the window has to relayout), and so in relayout we drain mPendingDrawHandlers
+ * into mReadyDrawHandlers. Finally once we get to finishDrawing we know everything in
+ * mReadyDrawHandlers corresponds to state which was observed by the client and we can
+ * invoke the consumers.
+ */
+ private final List<Consumer<SurfaceControl.Transaction>> mPendingDrawHandlers
+ = new ArrayList<>();
+ private final List<Consumer<SurfaceControl.Transaction>> mReadyDrawHandlers
+ = new ArrayList<>();
+
+ private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
+ finishSeamlessRotation(t);
+ updateSurfacePosition(t);
+ };
+
+ /**
+ * @see #setSurfaceTranslationY(int)
+ */
+ private int mSurfaceTranslationY;
+
+ /**
* Returns the visibility of the given {@link InternalInsetsType type} requested by the client.
*
* @param type the given {@link InternalInsetsType type}.
@@ -831,19 +862,27 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mPendingSeamlessRotate.unrotate(transaction, this);
getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
true /* seamlesslyRotated */);
+ applyWithNextDraw(mSeamlessRotationFinishedConsumer);
}
}
- void finishSeamlessRotation(boolean timeout) {
- if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.finish(this, timeout);
- mFinishSeamlessRotateFrameNumber = getFrameNumber();
- mPendingSeamlessRotate = null;
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- false /* seamlesslyRotated */);
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.finishSeamlessRotation(timeout);
- }
+ void cancelSeamlessRotation() {
+ finishSeamlessRotation(getPendingTransaction());
+ }
+
+ void finishSeamlessRotation(SurfaceControl.Transaction t) {
+ if (mPendingSeamlessRotate == null) {
+ return;
+ }
+
+ mPendingSeamlessRotate.finish(t, this);
+ mFinishSeamlessRotateFrameNumber = getFrameNumber();
+ mPendingSeamlessRotate = null;
+
+ getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
+ false /* seamlesslyRotated */);
+ if (mControllableInsetProvider != null) {
+ mControllableInsetProvider.finishSeamlessRotation();
}
}
@@ -1050,18 +1089,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* scaling override set.
* @see CompatModePackages#getCompatScale
* @see android.content.res.CompatibilityInfo#supportsScreen
- * @see ActivityRecord#inSizeCompatMode()
+ * @see ActivityRecord#hasSizeCompatBounds()
*/
- boolean inSizeCompatMode() {
- return mOverrideScale != 1f || inSizeCompatMode(mAttrs, mActivityRecord);
+ boolean hasCompatScale() {
+ return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
}
/**
* @return {@code true} if the application runs in size compatibility mode.
* @see android.content.res.CompatibilityInfo#supportsScreen
- * @see ActivityRecord#inSizeCompatMode()
+ * @see ActivityRecord#hasSizeCompatBounds()
*/
- static boolean inSizeCompatMode(WindowManager.LayoutParams attrs, WindowToken windowToken) {
+ static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
|| (windowToken != null && windowToken.hasSizeCompatBounds()
// Exclude starting window because it is not displayed by the application.
@@ -1266,7 +1305,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
windowFrames.mCompatFrame.set(windowFrames.mFrame);
- if (inSizeCompatMode()) {
+ if (hasCompatScale()) {
// Also the scaled frame that we report to the app needs to be
// adjusted to be in its coordinate space.
windowFrames.mCompatFrame.scale(mInvGlobalScale);
@@ -1538,7 +1577,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
InsetsState getCompatInsetsState() {
InsetsState state = getInsetsState();
- if (inSizeCompatMode()) {
+ if (hasCompatScale()) {
state = new InsetsState(state, true);
state.scale(mInvGlobalScale);
}
@@ -1676,7 +1715,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void prelayout() {
- if (inSizeCompatMode()) {
+ if (hasCompatScale()) {
if (mOverrideScale != 1f) {
mGlobalScale = mToken.hasSizeCompatBounds()
? mToken.getSizeCompatScale() * mOverrideScale
@@ -3590,7 +3629,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void fillClientWindowFrames(ClientWindowFrames outFrames) {
outFrames.frame.set(mWindowFrames.mCompatFrame);
outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
- if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
+ if (mInvGlobalScale != 1.0f && hasCompatScale()) {
outFrames.displayFrame.scale(mInvGlobalScale);
}
@@ -3990,7 +4029,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
- proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
+ proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
proto.end(token);
}
@@ -4092,7 +4131,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.println(prefix + "mHasSurface=" + mHasSurface
+ " isReadyForDisplay()=" + isReadyForDisplay()
+ " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
- if (inSizeCompatMode()) {
+ if (hasCompatScale()) {
pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
}
if (dumpAll) {
@@ -4215,18 +4254,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float x, y;
int w,h;
- final boolean inSizeCompatMode = inSizeCompatMode();
+ final boolean hasCompatScale = hasCompatScale();
if ((mAttrs.flags & FLAG_SCALED) != 0) {
if (mAttrs.width < 0) {
w = pw;
- } else if (inSizeCompatMode) {
+ } else if (hasCompatScale) {
w = (int)(mAttrs.width * mGlobalScale + .5f);
} else {
w = mAttrs.width;
}
if (mAttrs.height < 0) {
h = ph;
- } else if (inSizeCompatMode) {
+ } else if (hasCompatScale) {
h = (int)(mAttrs.height * mGlobalScale + .5f);
} else {
h = mAttrs.height;
@@ -4234,21 +4273,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} else {
if (mAttrs.width == MATCH_PARENT) {
w = pw;
- } else if (inSizeCompatMode) {
+ } else if (hasCompatScale) {
w = (int)(mRequestedWidth * mGlobalScale + .5f);
} else {
w = mRequestedWidth;
}
if (mAttrs.height == MATCH_PARENT) {
h = ph;
- } else if (inSizeCompatMode) {
+ } else if (hasCompatScale) {
h = (int)(mRequestedHeight * mGlobalScale + .5f);
} else {
h = mRequestedHeight;
}
}
- if (inSizeCompatMode) {
+ if (hasCompatScale) {
x = mAttrs.x * mGlobalScale;
y = mAttrs.y * mGlobalScale;
} else {
@@ -4276,7 +4315,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// We need to make sure we update the CompatFrame as it is used for
// cropping decisions, etc, on systems where we lack a decor layer.
windowFrames.mCompatFrame.set(windowFrames.mFrame);
- if (inSizeCompatMode) {
+ if (hasCompatScale) {
// See comparable block in computeFrameLw.
windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
@@ -4394,7 +4433,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float translateToWindowX(float x) {
float winX = x - mWindowFrames.mFrame.left;
- if (inSizeCompatMode()) {
+ if (hasCompatScale()) {
winX *= mGlobalScale;
}
return winX;
@@ -4402,7 +4441,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float translateToWindowY(float y) {
float winY = y - mWindowFrames.mFrame.top;
- if (inSizeCompatMode()) {
+ if (hasCompatScale()) {
winY *= mGlobalScale;
}
return winY;
@@ -5324,6 +5363,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Expand for surface insets. See WindowState.expandForSurfaceInsets.
transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
+
+ outPoint.y += mSurfaceTranslationY;
}
/**
@@ -5331,7 +5372,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* scaled, the insets also need to be scaled for surface position in global coordinate.
*/
private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
- if (!inSizeCompatMode()) {
+ if (!hasCompatScale()) {
outPos.x = surfaceInsets.left;
outPos.y = surfaceInsets.top;
return;
@@ -5684,6 +5725,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
mActivityRecord.mRelaunchStartTime = 0;
}
+
+ executeDrawHandlers(postDrawTransaction);
if (!onSyncFinishedDrawing()) {
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
@@ -5698,6 +5741,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void immediatelyNotifyBlastSync() {
+ prepareDrawHandlers();
finishDrawing(null);
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
if (!useBLASTSync()) return;
@@ -5777,4 +5821,80 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
-attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
}
+
+ /**
+ * This method is used to control whether we return the BLAST_SYNC flag
+ * from relayoutWindow calls on this window (triggering the client to redirect
+ * it's next draw in to a transaction). If we have pending draw handlers, we are
+ * looking for the client to sync.
+ *
+ * See {@link WindowState#mPendingDrawHandlers}
+ */
+ @Override
+ boolean useBLASTSync() {
+ return super.useBLASTSync() || (mPendingDrawHandlers.size() != 0);
+ }
+
+ /**
+ * Apply the transaction with the next window redraw. A full relayout/finishDrawing
+ * cycle must occur before completion. This means if you call the function while
+ * "in relayout", the results may be undefined but at all other times the function
+ * should sort of transparently work like this:
+ * 1. Make changes to WM hierarchy (say change app configuration)
+ * 2. Call apply with next draw.
+ * 3. After finishDrawing, our consumer will be passed the Transaction
+ * containing the buffer, and we can merge in additional operations.
+ * See {@link WindowState#mPendingDrawHandlers}
+ */
+ void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
+ mPendingDrawHandlers.add(consumer);
+ requestRedrawForSync();
+
+ mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
+ BLAST_TIMEOUT_DURATION);
+ }
+
+ /**
+ * Called from relayout, to indicate the next "finishDrawing" will contain
+ * all changes applied by the time mPendingDrawHandlers was populated.
+ *
+ * See {@link WindowState#mPendingDrawHandlers}
+ */
+ void prepareDrawHandlers() {
+ mReadyDrawHandlers.addAll(mPendingDrawHandlers);
+ mPendingDrawHandlers.clear();
+ }
+
+ /**
+ * Drain the draw handlers, called from finishDrawing()
+ * See {@link WindowState#mPendingDrawHandlers}
+ */
+ boolean executeDrawHandlers(SurfaceControl.Transaction t) {
+ if (t == null) t = mTmpTransaction;
+ boolean hadHandlers = false;
+ for (int i = 0; i < mReadyDrawHandlers.size(); i++) {
+ mReadyDrawHandlers.get(i).accept(t);
+ hadHandlers = true;
+ }
+ mReadyDrawHandlers.clear();
+ mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+
+ t.apply();
+
+ return hadHandlers;
+ }
+
+ /**
+ * Adds an additional translation offset to be applied when positioning the surface. Used to
+ * correct offsets in specific reparenting situations, e.g. the navigation bar window attached
+ * on the lower split-screen app.
+ */
+ void setSurfaceTranslationY(int translationY) {
+ mSurfaceTranslationY = translationY;
+ }
+
+ @Override
+ @WindowManager.LayoutParams.WindowType int getWindowType() {
+ return mAttrs.type;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8867aa747379..5276d9c8a5f1 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -764,4 +764,9 @@ class WindowToken extends WindowContainer<WindowState> {
forAllWindows(WindowState::clearFrozenInsetsState, true /* traverseTopToBottom */);
}
}
+
+ @Override
+ @WindowManager.LayoutParams.WindowType int getWindowType() {
+ return windowType;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index e8b8bfce21a3..0bb97f560a1c 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -114,15 +114,6 @@ class WindowTracing {
* @param pw Print writer
*/
void stopTrace(@Nullable PrintWriter pw) {
- stopTrace(pw, true /* writeToFile */);
- }
-
- /**
- * Stops the trace
- * @param pw Print writer
- * @param writeToFile If the current buffer should be written to disk or not
- */
- void stopTrace(@Nullable PrintWriter pw, boolean writeToFile) {
if (IS_USER) {
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
return;
@@ -135,12 +126,35 @@ class WindowTracing {
logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
throw new IllegalStateException("tracing enabled while waiting for flush.");
}
- if (writeToFile) {
- writeTraceToFileLocked();
- logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+ writeTraceToFileLocked();
+ logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+ }
+ ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
+ }
+
+ /**
+ * Stops the trace and write the current buffer to disk then restart, if it's already running.
+ * @param pw Print writer
+ */
+ void saveForBugreport(@Nullable PrintWriter pw) {
+ if (IS_USER) {
+ logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
+ return;
+ }
+ synchronized (mEnabledLock) {
+ if (!mEnabled) {
+ return;
}
+ mEnabled = mEnabledLockFree = false;
+ logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
+ writeTraceToFileLocked();
+ logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+ ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
+ logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+ mBuffer.resetBuffer();
+ mEnabled = mEnabledLockFree = true;
+ ProtoLogImpl.getSingleInstance().startProtoLog(pw);
}
- ProtoLogImpl.getSingleInstance().stopProtoLog(pw, writeToFile);
}
private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
@@ -188,6 +202,9 @@ class WindowTracing {
case "stop":
stopTrace(pw);
return 0;
+ case "save-for-bugreport":
+ saveForBugreport(pw);
+ return 0;
case "status":
logAndPrintln(pw, getStatus());
return 0;
@@ -230,6 +247,7 @@ class WindowTracing {
pw.println("Window manager trace options:");
pw.println(" start: Start logging");
pw.println(" stop: Stop logging");
+ pw.println(" save-for-bugreport: Save logging data to file if it's running.");
pw.println(" frame: Log trace once per frame");
pw.println(" transaction: Log each transaction");
pw.println(" size: Set the maximum log size (in KB)");
@@ -316,19 +334,6 @@ class WindowTracing {
}
}
- /**
- * Writes the trace buffer to new file for the bugreport.
- *
- * This method is synchronized with {@code #startTrace(PrintWriter)} and
- * {@link #stopTrace(PrintWriter)}.
- */
- void writeTraceToFile() {
- synchronized (mEnabledLock) {
- writeTraceToFileLocked();
- }
- ProtoLogImpl.getSingleInstance().writeProtoLogToFile();
- }
-
private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
Log.i(TAG, msg);
if (pw != null) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 29bce792fe30..0a02a86e71a6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -49,7 +49,6 @@ cc_library_static {
"com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
"com_android_server_powerstats_PowerStatsService.cpp",
- "com_android_server_security_VerityUtils.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
"com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index a73f6c6d8c2d..6cb4a63a5636 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -123,16 +123,9 @@ static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /*
}
static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) {
- raise(BIONIC_SIGNAL_FDTRACK);
-
- // Wait for a bit to allow fdtrack to dump backtraces to logcat.
- std::this_thread::sleep_for(5s);
-
- // Abort on a different thread to avoid ART dumping runtime stacks.
- std::thread([]() {
- LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd "
- "backtraces");
- }).join();
+ sigval val;
+ val.sival_int = 1;
+ sigqueue(getpid(), BIONIC_SIGNAL_FDTRACK, val);
}
static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 10705af9ac38..be06d0395499 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1783,8 +1783,9 @@ static void nativeSetSystemUiLightsOut(JNIEnv* /* env */, jclass /* clazz */, jl
im->setSystemUiLightsOut(lightsOut);
}
-static jboolean nativeTransferTouchFocus(JNIEnv* env,
- jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) {
+static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jobject fromChannelTokenObj, jobject toChannelTokenObj,
+ jboolean isDragDrop) {
if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
return JNI_FALSE;
}
@@ -1793,8 +1794,8 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env,
sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- if (im->getInputManager()->getDispatcher()->transferTouchFocus(
- fromChannelToken, toChannelToken)) {
+ if (im->getInputManager()->getDispatcher()->transferTouchFocus(fromChannelToken, toChannelToken,
+ isDragDrop)) {
return JNI_TRUE;
} else {
return JNI_FALSE;
@@ -2267,7 +2268,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
(void*)nativeRequestPointerCapture},
{"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode},
{"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
- {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
+ {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z",
(void*)nativeTransferTouchFocus},
{"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
{"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index ef2d0baff031..f60b35499013 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -32,8 +32,6 @@
#include "com_android_server_vibrator_VibratorManagerService.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
namespace aidl = android::hardware::vibrator;
@@ -85,10 +83,11 @@ static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId)
class VibratorControllerWrapper {
public:
VibratorControllerWrapper(JNIEnv* env, int32_t vibratorId, jobject callbackListener)
- : mHal(std::move(findVibrator(vibratorId))),
+ : mHal(findVibrator(vibratorId)),
mVibratorId(vibratorId),
mCallbackListener(env->NewGlobalRef(callbackListener)) {
- LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator hal");
+ LOG_ALWAYS_FATAL_IF(mHal == nullptr,
+ "Failed to connect to vibrator HAL, or vibratorId is invalid");
LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
"Unable to create global reference to vibration callback handler");
}
@@ -130,15 +129,15 @@ static void destroyNativeWrapper(void* ptr) {
}
}
-static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jint vibratorId,
- jobject callbackListener) {
+static jlong vibratorNativeInit(JNIEnv* env, jclass /* clazz */, jint vibratorId,
+ jobject callbackListener) {
std::unique_ptr<VibratorControllerWrapper> wrapper =
std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener);
wrapper->hal()->init();
return reinterpret_cast<jlong>(wrapper.release());
}
-static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+static jlong vibratorGetNativeFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeWrapper));
}
@@ -286,25 +285,46 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr,
wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id));
}
+static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+ if (wrapper == nullptr) {
+ ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized");
+ return NAN;
+ }
+ auto result = wrapper->hal()->getResonantFrequency();
+ return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+}
+
+static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+ if (wrapper == nullptr) {
+ ALOGE("vibratorGetQFactor failed because native wrapper was not initialized");
+ return NAN;
+ }
+ auto result = wrapper->hal()->getQFactor();
+ return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+}
+
static const JNINativeMethod method_table[] = {
- {"vibratorInit",
+ {"nativeInit",
"(ILcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;)J",
- (void*)vibratorInit},
- {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
- {"vibratorIsAvailable", "(J)Z", (void*)vibratorIsAvailable},
- {"vibratorOn", "(JJJ)V", (void*)vibratorOn},
- {"vibratorOff", "(J)V", (void*)vibratorOff},
- {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
- {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
- {"vibratorPerformComposedEffect",
- "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
+ (void*)vibratorNativeInit},
+ {"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer},
+ {"isAvailable", "(J)Z", (void*)vibratorIsAvailable},
+ {"on", "(JJJ)V", (void*)vibratorOn},
+ {"off", "(J)V", (void*)vibratorOff},
+ {"setAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
+ {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
+ {"performComposedEffect", "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
(void*)vibratorPerformComposedEffect},
- {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
- {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
- {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
- {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
- {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
- {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
+ {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+ {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
+ {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
+ {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities},
+ {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
+ {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
+ {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency},
+ {"getQFactor", "(J)F", (void*)vibratorGetQFactor},
};
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -320,7 +340,8 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
- return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController",
+ return jniRegisterNativeMethods(env,
+ "com/android/server/vibrator/VibratorController$NativeWrapper",
method_table, NELEM(method_table));
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 1815f0cd44c9..03a01523b8e2 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,7 +52,6 @@ int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
-int register_android_server_security_VerityUtils(JNIEnv* env);
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
@@ -106,7 +105,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_hardware_display_DisplayViewport(env);
register_android_server_net_NetworkStatsFactory(env);
register_android_server_net_NetworkStatsService(env);
- register_android_server_security_VerityUtils(env);
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_LowMemDetector(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 6a8f6d419786..d43cf3f59170 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -45,13 +45,6 @@ xsd_config {
}
xsd_config {
- name: "cec-config",
- srcs: ["cec-config/cec-config.xsd"],
- api_dir: "cec-config/schema",
- package_name: "com.android.server.hdmi.cec.config",
-}
-
-xsd_config {
name: "device-state-config",
srcs: ["device-state-config/device-state-config.xsd"],
api_dir: "device-state-config/schema",
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
deleted file mode 100644
index b59c93cce332..000000000000
--- a/services/core/xsd/cec-config/cec-config.xsd
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xs:schema version="2.0"
- elementFormDefault="qualified"
- attributeFormDefault="unqualified"
- xmlns:xs="http://www.w3.org/2001/XMLSchema">
- <xs:element name="cec-settings">
- <xs:complexType>
- <xs:sequence>
- <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/>
- </xs:sequence>
- </xs:complexType>
- </xs:element>
- <xs:complexType name="setting">
- <xs:attribute name="name" type="xs:string"/>
- <xs:attribute name="value-type" type="xs:string"/>
- <xs:attribute name="user-configurable" type="xs:boolean"/>
- <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
- <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
- </xs:complexType>
- <xs:complexType name="value-list">
- <xs:sequence>
- <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/>
- </xs:sequence>
- </xs:complexType>
- <xs:complexType name="value">
- <xs:attribute name="string-value" type="xs:string"/>
- <xs:attribute name="int-value" type="xs:string"/>
- </xs:complexType>
-</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
deleted file mode 100644
index 75872d4fb8a7..000000000000
--- a/services/core/xsd/cec-config/schema/current.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-// Signature format: 2.0
-package com.android.server.hdmi.cec.config {
-
- public class CecSettings {
- ctor public CecSettings();
- method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting();
- }
-
- public class Setting {
- ctor public Setting();
- method public com.android.server.hdmi.cec.config.ValueList getAllowedValues();
- method public com.android.server.hdmi.cec.config.Value getDefaultValue();
- method public String getName();
- method public boolean getUserConfigurable();
- method public String getValueType();
- method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
- method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
- method public void setName(String);
- method public void setUserConfigurable(boolean);
- method public void setValueType(String);
- }
-
- public class Value {
- ctor public Value();
- method public String getIntValue();
- method public String getStringValue();
- method public void setIntValue(String);
- method public void setStringValue(String);
- }
-
- public class ValueList {
- ctor public ValueList();
- method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue();
- }
-
- public class XmlParser {
- ctor public XmlParser();
- method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- }
-
-}
-
diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/services/core/xsd/cec-config/schema/last_current.txt
+++ /dev/null
diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/services/core/xsd/cec-config/schema/last_removed.txt
+++ /dev/null
diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/services/core/xsd/cec-config/schema/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48ae8d672fb1..aed13b263a7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -19,6 +19,8 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -38,7 +40,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -455,8 +456,7 @@ class ActiveAdmin {
try {
trustAgentInfo.options.saveToXml(out);
} catch (XmlPullParserException e) {
- Log.e(DevicePolicyManagerService.LOG_TAG,
- "Failed to save TrustAgent options", e);
+ Slog.e(LOG_TAG, e, "Failed to save TrustAgent options");
}
out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
}
@@ -629,8 +629,7 @@ class ActiveAdmin {
String tag = parser.getName();
if (TAG_POLICIES.equals(tag)) {
if (shouldOverridePolicies) {
- Log.d(DevicePolicyManagerService.LOG_TAG,
- "Overriding device admin policies from XML.");
+ Slog.d(LOG_TAG, "Overriding device admin policies from XML.");
info.readPoliciesFromXml(parser);
}
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
@@ -726,16 +725,14 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
shortSupportMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading short support message");
+ Slog.w(LOG_TAG, "Missing text when loading short support message");
}
} else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
longSupportMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading long support message");
+ Slog.w(LOG_TAG, "Missing text when loading long support message");
}
} else if (TAG_PARENT_ADMIN.equals(tag)) {
Preconditions.checkState(!isParent);
@@ -748,8 +745,7 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
organizationName = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading organization name");
+ Slog.w(LOG_TAG, "Missing text when loading organization name");
}
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
@@ -758,16 +754,14 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
startUserSessionMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading start session message");
+ Slog.w(LOG_TAG, "Missing text when loading start session message");
}
} else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
endUserSessionMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading end session message");
+ Slog.w(LOG_TAG, "Missing text when loading end session message");
}
} else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
mCrossProfileCalendarPackages = readPackageList(parser, tag);
@@ -802,16 +796,14 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
mOrganizationId = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing Organization ID.");
+ Slog.w(LOG_TAG, "Missing Organization ID.");
}
} else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
mEnrollmentSpecificId = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing Enrollment-specific ID.");
+ Slog.w(LOG_TAG, "Missing Enrollment-specific ID.");
}
} else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
@@ -820,7 +812,7 @@ class ActiveAdmin {
mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
USB_DATA_SIGNALING_ENABLED_DEFAULT);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
+ Slog.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
}
}
@@ -842,12 +834,10 @@ class ActiveAdmin {
if (packageName != null) {
result.add(packageName);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Package name missing under " + outerTag);
+ Slog.w(LOG_TAG, "Package name missing under %s", outerTag);
}
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + outerTag);
+ Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
}
}
return result;
@@ -868,8 +858,7 @@ class ActiveAdmin {
if (tag.equals(tagDAM)) {
result.add(parser.getAttributeValue(null, ATTR_VALUE));
} else {
- Slog.e(DevicePolicyManagerService.LOG_TAG,
- "Expected tag " + tag + " but found " + tagDAM);
+ Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
}
}
}
@@ -891,8 +880,7 @@ class ActiveAdmin {
final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
result.put(component, trustAgentInfo);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + tagDAM);
+ Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
}
}
return result;
@@ -912,8 +900,7 @@ class ActiveAdmin {
if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
result.options = PersistableBundle.restoreFromXml(parser);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + tagDAM);
+ Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
}
}
return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index d812b8f7fadb..8027e5b9d9bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -33,7 +35,7 @@ import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -47,7 +49,6 @@ import java.security.cert.X509Certificate;
import java.util.List;
public class CertificateMonitor {
- protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
protected static final int MONITORING_CERT_NOTIFICATION_ID = SystemMessage.NOTE_SSL_CERT_INFO;
private final DevicePolicyManagerService mService;
@@ -78,16 +79,16 @@ public class CertificateMonitor {
X509Certificate cert = parseCert(certBuffer);
pemCert = Credentials.convertToPem(cert);
} catch (CertificateException | IOException ce) {
- Log.e(LOG_TAG, "Problem converting cert", ce);
+ Slog.e(LOG_TAG, "Problem converting cert", ce);
return null;
}
try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
return keyChainConnection.getService().installCaCertificate(pemCert);
} catch (RemoteException e) {
- Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+ Slog.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
} catch (InterruptedException e1) {
- Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+ Slog.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
Thread.currentThread().interrupt();
}
return null;
@@ -99,9 +100,9 @@ public class CertificateMonitor {
keyChainConnection.getService().deleteCaCertificate(aliases[i]);
}
} catch (RemoteException e) {
- Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
+ Slog.e(LOG_TAG, "from CaCertUninstaller: ", e);
} catch (InterruptedException ie) {
- Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+ Slog.w(LOG_TAG, "CaCertUninstaller: ", ie);
Thread.currentThread().interrupt();
}
}
@@ -137,7 +138,8 @@ public class CertificateMonitor {
};
private void updateInstalledCertificates(final UserHandle userHandle) {
- if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+ final int userId = userHandle.getIdentifier();
+ if (!mInjector.getUserManager().isUserUnlocked(userId)) {
return;
}
@@ -145,7 +147,8 @@ public class CertificateMonitor {
try {
installedCerts = getInstalledCaCertificates(userHandle);
} catch (RemoteException | RuntimeException e) {
- Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+ Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
+ userId);
return;
}
mService.onInstalledCertificatesChanged(userHandle, installedCerts);
@@ -167,7 +170,7 @@ public class CertificateMonitor {
try {
userContext = mInjector.createContextAsUser(userHandle);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+ Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle);
return null;
}
@@ -183,7 +186,6 @@ public class CertificateMonitor {
smallIconId = R.drawable.stat_sys_certificate_info;
parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
} else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
- final String ownerName = mService.getDeviceOwnerName();
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
mService.getDeviceOwnerName());
smallIconId = R.drawable.stat_sys_certificate_info;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 3067d4507162..00e0292d404f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -46,19 +46,11 @@ public class DeviceAdminServiceController {
final Object mLock = new Object();
final Context mContext;
- private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
private final DevicePolicyConstants mConstants;
private final Handler mHandler; // needed?
- static void debug(String format, Object... args) {
- if (!DEBUG) {
- return;
- }
- Slog.d(TAG, String.format(format, args));
- }
-
private class DevicePolicyServiceConnection
extends PersistentConnection<IDeviceAdminService> {
public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
@@ -88,7 +80,6 @@ public class DeviceAdminServiceController {
public DeviceAdminServiceController(DevicePolicyManagerService service,
DevicePolicyConstants constants) {
- mService = service;
mInjector = service.mInjector;
mContext = mInjector.mContext;
mHandler = new Handler(BackgroundThread.get().getLooper());
@@ -122,8 +113,9 @@ public class DeviceAdminServiceController {
synchronized (mLock) {
final ServiceInfo service = findService(packageName, userId);
if (service == null) {
- debug("Owner package %s on u%d has no service.",
- packageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId);
+ }
disconnectServiceOnUserLocked(userId, actionForLog);
return;
}
@@ -134,14 +126,17 @@ public class DeviceAdminServiceController {
// Note even when we're already connected to the same service, the binding
// would have died at this point due to a package update. So we disconnect
// anyway and re-connect.
- debug("Disconnecting from existing service connection.",
- packageName, userId);
+ if (DEBUG) {
+ Slog.d("Disconnecting from existing service connection.", packageName,
+ userId);
+ }
disconnectServiceOnUserLocked(userId, actionForLog);
}
- debug("Owner package %s on u%d has service %s for %s",
- packageName, userId,
+ if (DEBUG) {
+ Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId,
service.getComponentName().flattenToShortString(), actionForLog);
+ }
final DevicePolicyServiceConnection conn =
new DevicePolicyServiceConnection(
@@ -172,8 +167,10 @@ public class DeviceAdminServiceController {
private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
final DevicePolicyServiceConnection conn = mConnections.get(userId);
if (conn != null) {
- debug("Stopping service for u%d if already running for %s.",
- userId, actionForLog);
+ if (DEBUG) {
+ Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId,
+ actionForLog);
+ }
conn.unbind();
mConnections.remove(userId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 464d6f5ea835..84e6da0d9851 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -88,7 +88,7 @@ public class DevicePolicyConstants {
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
- Slog.e(TAG, "Bad device policy settings: " + settings);
+ Slog.e(TAG, "Bad device policy settings: %s", settings);
}
long dasDiedServiceReconnectBackoffSec = parser.getLong(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index c0b2ed4cc955..52cdce6ee7b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -179,11 +179,11 @@ class DevicePolicyData {
*/
static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) {
FileOutputStream stream = null;
+ File chooseForWrite = null;
try {
- File chooseForWrite = file.chooseForWrite();
+ chooseForWrite = file.chooseForWrite();
if (VERBOSE_LOG) {
- Slog.v(TAG, "Storing data for user " + policyData.mUserId + " on "
- + chooseForWrite);
+ Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
}
stream = new FileOutputStream(chooseForWrite, false);
TypedXmlSerializer out = Xml.resolveSerializer(stream);
@@ -195,7 +195,7 @@ class DevicePolicyData {
policyData.mRestrictionsProvider.flattenToString());
}
if (policyData.mUserSetupComplete) {
- if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true");
+ if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
}
if (policyData.mPaired) {
@@ -216,8 +216,8 @@ class DevicePolicyData {
if (policyData.mFactoryResetFlags != 0) {
if (VERBOSE_LOG) {
- Slog.v(TAG, "Storing factory reset flags for user " + policyData.mUserId + ": "
- + factoryResetFlagsToString(policyData.mFactoryResetFlags));
+ Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
+ factoryResetFlagsToString(policyData.mFactoryResetFlags));
}
out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
}
@@ -382,7 +382,7 @@ class DevicePolicyData {
file.commit();
return true;
} catch (XmlPullParserException | IOException e) {
- Slog.w(TAG, "failed writing file", e);
+ Slog.w(TAG, e, "failed writing file %s", chooseForWrite);
try {
if (stream != null) {
stream.close();
@@ -404,10 +404,8 @@ class DevicePolicyData {
ComponentName ownerComponent) {
FileInputStream stream = null;
File file = journaledFile.chooseForRead();
- if (VERBOSE_LOG) {
- Slog.v(TAG, "Loading data for user " + policy.mUserId + " from " + file);
- }
-
+ if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
+ boolean needsRewrite = false;
try {
stream = new FileInputStream(file);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -454,8 +452,8 @@ class DevicePolicyData {
policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
if (VERBOSE_LOG) {
- Slog.v(TAG, "Restored factory reset flags for user " + policy.mUserId + ": "
- + factoryResetFlagsToString(policy.mFactoryResetFlags));
+ Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
+ factoryResetFlagsToString(policy.mFactoryResetFlags));
}
policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
@@ -488,7 +486,7 @@ class DevicePolicyData {
policy.mAdminMap.put(ap.info.getComponent(), ap);
}
} catch (RuntimeException e) {
- Slog.w(TAG, "Failed loading admin " + name, e);
+ Slog.w(TAG, e, "Failed loading admin %s", name);
}
} else if ("delegation".equals(tag)) {
// Parse delegation info.
@@ -560,7 +558,7 @@ class DevicePolicyData {
policy.mAppsSuspended =
parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else {
- Slog.w(TAG, "Unknown tag: " + tag);
+ Slog.w(TAG, "Unknown tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
}
}
@@ -568,7 +566,7 @@ class DevicePolicyData {
// Don't be noisy, this is normal if we haven't defined any policies.
} catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
| IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, e, "failed parsing %s", file);
}
try {
if (stream != null) {
@@ -592,8 +590,8 @@ class DevicePolicyData {
}
}
if (!haveOwner) {
- Slog.w(TAG, "Previous password owner " + mPasswordOwner
- + " no longer active; disabling");
+ Slog.w(TAG, "Previous password owner %s no longer active; disabling",
+ mPasswordOwner);
mPasswordOwner = -1;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a3dadd835202..70756787c561 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -473,7 +473,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// to decide whether an existing policy in the {@link #DEVICE_POLICIES_XML} needs to
// be upgraded. See {@link PolicyVersionUpgrader} on instructions how to add an upgrade
// step.
- static final int DPMS_VERSION = 1;
+ static final int DPMS_VERSION = 2;
static {
SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
@@ -1056,7 +1056,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
service.removeCredentialManagementApp();
}
} catch (RemoteException | InterruptedException | IllegalStateException e) {
- Log.e(LOG_TAG, "Unable to remove the credential management app");
+ Slog.e(LOG_TAG, "Unable to remove the credential management app");
}
});
}
@@ -1106,7 +1106,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
*/
void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
- Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker));
+ Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
mSafetyChecker = safetyChecker;
mInjector.setDevicePolicySafetyChecker(safetyChecker);
}
@@ -1149,18 +1149,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@OperationSafetyReason int reason) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
- Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
+ Slog.i(LOG_TAG, "setNextOperationSafety(%s, %s)",
DevicePolicyManager.operationToString(operation),
- DevicePolicyManager.operationSafetyReasonToString(reason)));
+ DevicePolicyManager.operationSafetyReasonToString(reason));
mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
}
@Override
public boolean isSafeOperation(@OperationSafetyReason int reason) {
if (VERBOSE_LOG) {
- Slog.v(LOG_TAG, "checking isSafeOperation("
- + DevicePolicyManager.operationSafetyReasonToString(reason)
- + ") using mSafetyChecker " + mSafetyChecker);
+ Slog.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s",
+ DevicePolicyManager.operationSafetyReasonToString(reason), mSafetyChecker);
}
return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason);
}
@@ -1588,7 +1587,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
CryptoTestHelper.runAndLogSelfTest();
}
- public String[] getPersonalAppsForSuspension(int userId) {
+ public String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
return PersonalAppsSuspensionHelper.forUser(mContext, userId)
.getPersonalAppsForSuspension();
}
@@ -1893,9 +1892,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
- Slog.i(LOG_TAG, String.format(
- "Migrating COMP to PO on a corp owned device; primary user: %d; profile: %d",
- doUserId, poUserId));
+ Slog.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; "
+ + "profile: %d", doUserId, poUserId);
Slog.i(LOG_TAG, "Giving the PO additional power...");
markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
@@ -1940,11 +1938,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void uninstallOrDisablePackage(String packageName, int userHandle) {
+ private void uninstallOrDisablePackage(String packageName, @UserIdInt int userId) {
final ApplicationInfo appInfo;
try {
appInfo = mIPackageManager.getApplicationInfo(
- packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+ packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
} catch (RemoteException e) {
// Shouldn't happen.
return;
@@ -1954,10 +1952,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- Slog.i(LOG_TAG, String.format(
- "Package %s is pre-installed, marking disabled until used", packageName));
+ Slog.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used",
+ packageName);
mContext.getPackageManager().setApplicationEnabledSetting(packageName,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0 /* flags */);
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0);
return;
}
@@ -1968,17 +1966,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int status = intent.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
- Slog.i(LOG_TAG, String.format(
- "Package %s uninstalled for user %d", packageName, userHandle));
+ Slog.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId);
} else {
- Slog.e(LOG_TAG, String.format(
- "Failed to uninstall %s; status: %d", packageName, status));
+ Slog.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status);
}
}
};
- final PackageInstaller pi = mInjector.getPackageManager(userHandle).getPackageInstaller();
- pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender));
+ final PackageInstaller pi = mInjector.getPackageManager(userId).getPackageInstaller();
+ pi.uninstall(packageName, /* flags= */ 0, new IntentSender((IIntentSender) mLocalSender));
}
@GuardedBy("getLockObject()")
@@ -2177,7 +2173,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
!mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
mOwners.writeDeviceOwner();
if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Device owner component filled in");
+ Slog.v(LOG_TAG, "Device owner component filled in");
}
}
}
@@ -2192,7 +2188,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// except for the "system controlled" ones.
if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) {
if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Migrating DO user restrictions");
+ Slog.v(LOG_TAG, "Migrating DO user restrictions");
}
migrated = true;
@@ -2220,7 +2216,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = ui.id;
if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) {
if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Migrating PO user restrictions for user " + userId);
+ Slog.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId);
}
migrated = true;
@@ -2243,7 +2239,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
if (VERBOSE_LOG && migrated) {
- Log.v(LOG_TAG, "User restrictions migrated.");
+ Slog.v(LOG_TAG, "User restrictions migrated.");
}
}
@@ -2271,9 +2267,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "origRestrictions=" + origRestrictions);
- Log.v(LOG_TAG, "newBaseRestrictions=" + newBaseRestrictions);
- Log.v(LOG_TAG, "newOwnerRestrictions=" + newOwnerRestrictions);
+ Slog.v(LOG_TAG, "origRestrictions=%s", origRestrictions);
+ Slog.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions);
+ Slog.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions);
}
mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(),
newBaseRestrictions);
@@ -2768,9 +2764,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private JournaledFile makeJournaledFile(@UserIdInt int userId, String fileName) {
final String base = new File(getPolicyFileDirectory(userId), fileName)
.getAbsolutePath();
- if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Opening " + base);
- }
+ if (VERBOSE_LOG) Slog.v(LOG_TAG, "Opening %s", base);
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -2969,8 +2963,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private class DpmsUpgradeDataProvider implements PolicyUpgraderDataProvider {
@Override
- public boolean isUserDeviceOwner(int userId) {
- return mOwners.isDeviceOwnerUserId(userId);
+ public boolean isDeviceOwner(int userId, ComponentName who) {
+ return mOwners.isDeviceOwnerUserId(userId)
+ && mOwners.getDeviceOwnerComponent().equals(who);
}
@Override
@@ -2998,14 +2993,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return component -> findAdmin(component, userId, /* throwForMissingPermission= */
false);
}
+
+ @Override
+ public int[] getUsersForUpgrade() {
+ List<UserInfo> allUsers = mUserManager.getUsers();
+ return allUsers.stream().mapToInt(u -> u.id).toArray();
+ }
}
private void performPolicyVersionUpgrade() {
- List<UserInfo> allUsers = mUserManager.getUsers();
PolicyVersionUpgrader upgrader = new PolicyVersionUpgrader(
new DpmsUpgradeDataProvider());
- upgrader.upgradePolicy(allUsers.stream().mapToInt(u -> u.id).toArray(), DPMS_VERSION);
+ upgrader.upgradePolicy(DPMS_VERSION);
}
private void revertTransferOwnershipIfNecessaryLocked() {
@@ -4914,8 +4914,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (!validationErrors.isEmpty()) {
- Log.w(LOG_TAG, "Failed to reset password due to constraint violation: "
- + validationErrors.get(0));
+ Slog.w(LOG_TAG, "Failed to reset password due to constraint violation: %s",
+ validationErrors.get(0));
return false;
}
}
@@ -5343,7 +5343,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
if (alias == null) {
- Log.w(LOG_TAG, "Problem installing cert");
+ Slog.w(LOG_TAG, "Problem installing cert");
return false;
}
@@ -5416,12 +5416,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
return true;
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Installing certificate", e);
+ Slog.e(LOG_TAG, "Installing certificate", e);
} finally {
keyChainConnection.close();
}
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while installing certificate", e);
+ Slog.w(LOG_TAG, "Interrupted while installing certificate", e);
Thread.currentThread().interrupt();
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -5466,12 +5466,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
return keyChain.removeKeyPair(alias);
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Removing keypair", e);
+ Slog.e(LOG_TAG, "Removing keypair", e);
} finally {
keyChainConnection.close();
}
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while removing keypair", e);
+ Slog.w(LOG_TAG, "Interrupted while removing keypair", e);
Thread.currentThread().interrupt();
} finally {
Binder.restoreCallingIdentity(id);
@@ -5489,9 +5489,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
return keyChainConnection.getService().containsKeyPair(alias);
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Querying keypair", e);
+ Slog.e(LOG_TAG, "Querying keypair", e);
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while querying keypair", e);
+ Slog.w(LOG_TAG, "Interrupted while querying keypair", e);
Thread.currentThread().interrupt();
}
return false;
@@ -5533,7 +5533,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return false;
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Querying grant to wifi auth. ", e);
+ Slog.e(LOG_TAG, "Querying grant to wifi auth.", e);
return false;
}
});
@@ -5574,11 +5574,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
keyChain.setGrant(granteeUid, alias, hasGrant);
return true;
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Setting grant for package.", e);
+ Slog.e(LOG_TAG, "Setting grant for package.", e);
return false;
}
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while setting key grant", e);
+ Slog.w(LOG_TAG, "Interrupted while setting key grant", e);
Thread.currentThread().interrupt();
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -5615,9 +5615,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return result;
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Querying keypair grants", e);
+ Slog.e(LOG_TAG, "Querying keypair grants", e);
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while querying keypair grants", e);
+ Slog.w(LOG_TAG, "Interrupted while querying keypair grants", e);
Thread.currentThread().interrupt();
}
return Collections.emptyList();
@@ -5742,7 +5742,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// As the caller will be granted access to the key, ensure no UID was specified, as
// it will not have the desired effect.
if (keySpec.getUid() != KeyStore.UID_SELF) {
- Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
+ Slog.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
logGenerateKeyPairFailure(caller, isCredentialManagementApp);
return false;
}
@@ -5768,8 +5768,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int generationResult = keyChain.generateKeyPair(algorithm,
new ParcelableKeyGenParameterSpec(keySpec));
if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
- Log.e(LOG_TAG, String.format(
- "KeyChain failed to generate a keypair, error %d.", generationResult));
+ Slog.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.",
+ generationResult);
logGenerateKeyPairFailure(caller, isCredentialManagementApp);
switch (generationResult) {
case KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE:
@@ -5808,7 +5808,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts));
} catch (CertificateException e) {
logGenerateKeyPairFailure(caller, isCredentialManagementApp);
- Log.e(LOG_TAG, "While retrieving certificate chain.", e);
+ Slog.e(LOG_TAG, "While retrieving certificate chain.", e);
return false;
}
@@ -5823,9 +5823,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return true;
}
} catch (RemoteException e) {
- Log.e(LOG_TAG, "KeyChain error while generating a keypair", e);
+ Slog.e(LOG_TAG, "KeyChain error while generating a keypair", e);
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while generating keypair", e);
+ Slog.w(LOG_TAG, "Interrupted while generating keypair", e);
Thread.currentThread().interrupt();
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -5883,10 +5883,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
return true;
} catch (InterruptedException e) {
- Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
+ Slog.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
Thread.currentThread().interrupt();
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Failed setting keypair certificate", e);
+ Slog.e(LOG_TAG, "Failed setting keypair certificate", e);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -5960,7 +5960,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
} catch (Exception e) {
// Caller could throw RuntimeException or RemoteException back across processes. Catch
// everything just to be sure.
- Log.e(LOG_TAG, "error while responding to callback", e);
+ Slog.e(LOG_TAG, "error while responding to callback", e);
}
}
@@ -6317,7 +6317,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean isUserSelectable) {
// Should not be user selectable
if (isUserSelectable) {
- Log.e(LOG_TAG, "The credential management app is not allowed to install a "
+ Slog.e(LOG_TAG, "The credential management app is not allowed to install a "
+ "user selectable key pair");
return false;
}
@@ -6517,8 +6517,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Persist the request so the device is automatically factory-reset on next start if
// the system crashes or reboots before the {@code DevicePolicySafetyChecker} calls
// its callback.
- Slog.i(LOG_TAG, String.format("Persisting factory reset request as it could be "
- + "delayed by %s", mSafetyChecker));
+ Slog.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s",
+ mSafetyChecker);
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
policy.setDelayedFactoryReset(reason, wipeExtRequested, wipeEuicc,
@@ -6632,8 +6632,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
int userId = admin != null ? admin.getUserHandle().getIdentifier()
: caller.getUserId();
- Slog.i(LOG_TAG, String.format("wipeDataWithReason(%s): admin=%s, user=%d",
- wipeReasonForUser, admin, userId));
+ Slog.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
+ userId);
if (calledByProfileOwnerOnOrgOwnedDevice) {
// When wipeData is called on the parent instance, it implies wiping the entire device.
if (calledOnParentInstance) {
@@ -7433,7 +7433,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
} catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
+ Slog.w(LOG_TAG, "Unable to notify WindowManager.", e);
}
});
}
@@ -8811,6 +8811,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor);
+ if (supervisorComponent == null) {
+ return null;
+ }
if (supervisorComponent.equals(doComponent) || supervisorComponent.equals(
poComponent)) {
return supervisorComponent;
@@ -8897,19 +8900,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// thrown but null data can be returned; if the appInfo for the specified package cannot
// be found then return false to prevent crashing the app.
if (appInfo == null) {
- Log.w(LOG_TAG,
- String.format("appInfo could not be found for package %s", packageName));
+ Slog.w(LOG_TAG, "appInfo could not be found for package %s", packageName);
return false;
} else if (uid != appInfo.uid) {
String message = String.format("Package %s (uid=%d) does not match provided uid %d",
packageName, appInfo.uid, uid);
- Log.w(LOG_TAG, message);
+ Slog.w(LOG_TAG, message);
throw new SecurityException(message);
}
} catch (RemoteException e) {
// If an exception is caught obtaining the appInfo just return false to prevent crashing
// apps due to an internal error.
- Log.e(LOG_TAG, "Exception caught obtaining appInfo for package " + packageName, e);
+ Slog.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName);
return false;
}
return true;
@@ -8925,7 +8927,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
String message = String.format(
"Calling uid %d, pid %d cannot check device identifier access for package %s "
+ "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid);
- Log.w(LOG_TAG, message);
+ Slog.w(LOG_TAG, message);
throw new SecurityException(message);
}
}
@@ -8933,14 +8935,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Canonical name for a given package.
*/
- private String getApplicationLabel(String packageName, int userHandle) {
+ private String getApplicationLabel(String packageName, @UserIdInt int userId) {
return mInjector.binderWithCleanCallingIdentity(() -> {
final Context userContext;
try {
- UserHandle handle = new UserHandle(userHandle);
- userContext = mContext.createPackageContextAsUser(packageName, 0, handle);
+ UserHandle userHandle = UserHandle.of(userId);
+ userContext = mContext.createPackageContextAsUser(packageName, /* flags= */ 0,
+ userHandle);
} catch (PackageManager.NameNotFoundException nnfe) {
- Log.w(LOG_TAG, packageName + " is not installed for user " + userHandle, nnfe);
+ Slog.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId);
return null;
}
ApplicationInfo appInfo = userContext.getApplicationInfo();
@@ -9329,6 +9332,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
dumpResources(pw, mContext, "vendor_cross_profile_apps", R.array.vendor_cross_profile_apps);
dumpResources(pw, mContext, "config_packagesExemptFromSuspension",
R.array.config_packagesExemptFromSuspension);
+ dumpResources(pw, mContext, "policy_exempt_apps", R.array.policy_exempt_apps);
+ dumpResources(pw, mContext, "vendor_policy_exempt_apps", R.array.vendor_policy_exempt_apps);
pw.decreaseIndent();
pw.println();
}
@@ -9557,9 +9562,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
result.add(info.options);
} else {
- Log.w(LOG_TAG, "Ignoring admin " + active.info
- + " because it has trust options but doesn't declare "
- + "KEYGUARD_DISABLE_TRUST_AGENTS");
+ Slog.w(LOG_TAG, "Ignoring admin %s because it has trust options but doesn't"
+ + " declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info);
}
} else if (disablesTrust) {
allAdminsHaveOptions = false;
@@ -9697,7 +9701,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
userIdToCheck);
systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} catch (RemoteException e) {
- Log.i(LOG_TAG, "Can't talk to package managed", e);
+ Slog.i(LOG_TAG, "Can't talk to package managed", e);
}
if (!systemService && !permittedList.contains(enabledPackage)) {
return false;
@@ -10178,7 +10182,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
user = userInfo.getUserHandle();
}
} catch (UserManager.CheckedUserOperationException e) {
- Log.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
+ Slog.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
}
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -10249,8 +10253,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} catch (RemoteException e) {
// Does not happen, same process
- Slog.wtf(LOG_TAG, String.format("Failed to install admin package %s for user %d",
- adminPkg, userId), e);
+ Slog.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
+ adminPkg, userId);
}
// Set admin.
@@ -10299,7 +10303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
/* managedUser= */ userId, /* adminExtras= */ null, /* showDisclaimer= */ true);
} else {
- Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer");
+ Slog.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId);
setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED);
}
}
@@ -10355,8 +10359,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
: UserManager.DISALLOW_REMOVE_USER;
if (isAdminAffectedByRestriction(who, restriction, caller.getUserId())) {
- Log.w(LOG_TAG, "The device owner cannot remove a user because "
- + restriction + " is enabled, and was not set by the device owner");
+ Slog.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and "
+ + "was not set by the device owner", restriction);
return false;
}
return mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle.getIdentifier());
@@ -10393,7 +10397,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return mInjector.getIActivityManager().switchUser(userId);
} catch (RemoteException e) {
- Log.e(LOG_TAG, "Couldn't switch user", e);
+ Slog.e(LOG_TAG, "Couldn't switch user", e);
return false;
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -10411,19 +10415,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
- Log.w(LOG_TAG, "Managed profile cannot be started in background");
+ Slog.w(LOG_TAG, "Managed profile cannot be started in background");
return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
final long id = mInjector.binderClearCallingIdentity();
try {
if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
- Log.w(LOG_TAG, "Cannot start user " + userId + ", too many users in background");
+ Slog.w(LOG_TAG, "Cannot start user %d, too many users in background", userId);
return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
}
if (mInjector.getIActivityManager().startUserInBackground(userId)) {
- Log.i(LOG_TAG, "Started used " + userId + " in background");
+ Slog.i(LOG_TAG, "Started used %d in background", userId);
return UserManager.USER_OPERATION_SUCCESS;
} else {
return UserManager.USER_OPERATION_ERROR_UNKNOWN;
@@ -10446,7 +10450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
- Log.w(LOG_TAG, "Managed profile cannot be stopped");
+ Slog.w(LOG_TAG, "Managed profile cannot be stopped");
return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
@@ -10469,14 +10473,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (isManagedProfile(callingUserId)) {
- Log.w(LOG_TAG, "Managed profile cannot be logout");
+ Slog.w(LOG_TAG, "Managed profile cannot be logout");
return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
final long id = mInjector.binderClearCallingIdentity();
try {
if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
- Log.w(LOG_TAG, "Failed to switch to primary user");
+ Slog.w(LOG_TAG, "Failed to switch to primary user");
// This should never happen as target user is UserHandle.USER_SYSTEM
return UserManager.USER_OPERATION_ERROR_UNKNOWN;
}
@@ -10612,6 +10616,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public List<String> listPolicyExemptApps() {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+ // TODO(b/181238156): decide whether it should only list the apps set by the resources,
+ // or also the "critical" apps defined by PersonalAppsSuspensionHelper (like SMS app).
+ // If it's the latter, refactor PersonalAppsSuspensionHelper so it (or a superclass) takes
+ // the resources on constructor.
+ String[] core = mContext.getResources().getStringArray(R.array.policy_exempt_apps);
+ String[] vendor = mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps);
+
+ int size = core.length + vendor.length;
+ Set<String> apps = new ArraySet<>(size);
+ for (String app : core) {
+ apps.add(app);
+ }
+ for (String app : vendor) {
+ apps.add(app);
+ }
+
+ return new ArrayList<>(apps);
+ }
+
+ @Override
public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
boolean parent) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -11229,8 +11257,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (isCrossProfileQuickContactDisabled(managedUserId)) {
if (VERBOSE_LOG) {
- Log.v(LOG_TAG,
- "Cross-profile contacts access disabled for user " + managedUserId);
+ Slog.v(LOG_TAG, "Cross-profile contacts access disabled for user %d",
+ managedUserId);
}
return;
}
@@ -11243,7 +11271,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* @return true if cross-profile QuickContact is disabled
*/
- private boolean isCrossProfileQuickContactDisabled(int userId) {
+ private boolean isCrossProfileQuickContactDisabled(@UserIdInt int userId) {
return getCrossProfileCallerIdDisabledForUser(userId)
&& getCrossProfileContactsSearchDisabledForUser(userId);
}
@@ -11252,23 +11280,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* @return the user ID of the managed user that is linked to the current user, if any.
* Otherwise -1.
*/
- public int getManagedUserId(int callingUserId) {
- if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId);
- }
+ public int getManagedUserId(@UserIdInt int callingUserId) {
+ if (VERBOSE_LOG) Slog.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId);
for (UserInfo ui : mUserManager.getProfiles(callingUserId)) {
if (ui.id == callingUserId || !ui.isManagedProfile()) {
continue; // Caller user self, or not a managed profile. Skip.
}
- if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Managed user=" + ui.id);
- }
+ if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user=%d", ui.id);
return ui.id;
}
- if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Managed user not found.");
- }
+ if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user not found.");
return -1;
}
@@ -11567,7 +11589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Some settings are no supported any more. However we do not want to throw a
// SecurityException to avoid breaking apps.
if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) {
- Log.i(LOG_TAG, "Global setting no longer supported: " + setting);
+ Slog.i(LOG_TAG, "Global setting no longer supported: %s", setting);
return;
}
@@ -12303,7 +12325,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING,
UserHandle.of(userId))) {
- Log.e(LOG_TAG, "printing is enabled");
+ Slog.e(LOG_TAG, "printing is enabled for user %d", userId);
return null;
}
String ownerPackage = mOwners.getProfileOwnerPackage(userId);
@@ -12316,22 +12338,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
return pm.getPackageInfo(packageName, 0);
} catch (NameNotFoundException e) {
- Log.e(LOG_TAG, "getPackageInfo error", e);
+ Slog.e(LOG_TAG, "getPackageInfo error", e);
return null;
}
});
if (packageInfo == null) {
- Log.e(LOG_TAG, "packageInfo is inexplicably null");
+ Slog.e(LOG_TAG, "packageInfo is inexplicably null");
return null;
}
ApplicationInfo appInfo = packageInfo.applicationInfo;
if (appInfo == null) {
- Log.e(LOG_TAG, "appInfo is inexplicably null");
+ Slog.e(LOG_TAG, "appInfo is inexplicably null");
return null;
}
CharSequence appLabel = pm.getApplicationLabel(appInfo);
if (appLabel == null) {
- Log.e(LOG_TAG, "appLabel is inexplicably null");
+ Slog.e(LOG_TAG, "appLabel is inexplicably null");
return null;
}
return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
@@ -12385,9 +12407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(intent);
Objects.requireNonNull(parentHandle);
final int userId = parentHandle.getIdentifier();
- Slog.i(LOG_TAG,
- String.format("Sending %s broadcast to manifest receivers.",
- intent.getAction()));
+ Slog.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction());
try {
final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers(
intent, /* resolvedType= */ null,
@@ -12397,9 +12417,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (checkCrossProfilePackagePermissions(packageName, userId,
requiresPermission)
|| checkModifyQuietModePermission(packageName, userId)) {
- Slog.i(LOG_TAG,
- String.format("Sending %s broadcast to %s.", intent.getAction(),
- packageName));
+ Slog.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(),
+ packageName);
final Intent packageIntent = new Intent(intent)
.setComponent(receiver.getComponentInfo().getComponentName())
.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
@@ -12407,9 +12426,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
} catch (RemoteException ex) {
- Slog.w(LOG_TAG,
- String.format("Cannot get list of broadcast receivers for %s because: %s.",
- intent.getAction(), ex));
+ Slog.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.",
+ intent.getAction(), ex);
}
}
@@ -12427,9 +12445,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */
-1, /* exported= */ true);
} catch (NameNotFoundException ex) {
- Slog.w(LOG_TAG,
- String.format("Cannot find the package %s to check for permissions.",
- packageName));
+ Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
+ packageName);
return false;
}
}
@@ -12458,9 +12475,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission(
packageName, userId);
} catch (NameNotFoundException ex) {
- Slog.w(LOG_TAG,
- String.format("Cannot find the package %s to check for permissions.",
- packageName));
+ Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
+ packageName);
return false;
}
}
@@ -12534,8 +12550,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// TODO(b/178494483): use EventLog instead
// TODO(b/178494483): log metrics?
if (VERBOSE_LOG) {
- Slog.v(LOG_TAG, String.format("notifyUnsafeOperationStateChanged(): %s=%b",
- DevicePolicyManager.operationSafetyReasonToString(reason), isSafe));
+ Slog.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b",
+ DevicePolicyManager.operationSafetyReasonToString(reason), isSafe);
}
Preconditions.checkArgument(mSafetyChecker == checker,
"invalid checker: should be %s, was %s", mSafetyChecker, checker);
@@ -12817,7 +12833,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
runningUserIds = mInjector.getIActivityManager().getRunningUserIds();
} catch (RemoteException e) {
// Shouldn't happen.
- Log.e(LOG_TAG, "Could not retrieve the list of running users", e);
+ Slog.e(LOG_TAG, "Could not retrieve the list of running users", e);
return;
}
// Send broadcasts to corresponding profile owners if any.
@@ -13178,9 +13194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
? UserHandle.USER_SYSTEM
: callingUserId;
- Slog.i(LOG_TAG,
- String.format("Calling user %d, device owner will be set on user %d",
- callingUserId, deviceOwnerUserId));
+ Slog.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
+ callingUserId, deviceOwnerUserId);
// hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
deviceOwnerUserId, callingUserId, /* isAdb= */ false,
@@ -13210,9 +13225,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
if (mUserManager.getUserInfo(callingUserId).isProfile()) {
- Slog.i(LOG_TAG,
- String.format("Calling user %d is a profile, cannot add another.",
- callingUserId));
+ Slog.i(LOG_TAG, "Calling user %d is a profile, cannot add another.", callingUserId);
// The check is called from inside a managed profile. A managed profile cannot
// be provisioned from within another managed profile.
return CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -13225,16 +13238,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Do not allow adding a managed profile if there's a restriction.
if (addingProfileRestricted) {
- Slog.i(LOG_TAG, String.format(
- "Adding a profile is restricted: User %s Has device owner? %b",
- callingUserHandle, hasDeviceOwner));
+ Slog.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
+ callingUserHandle, hasDeviceOwner);
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
// Bail out if we are trying to provision a work profile but one already exists.
if (!mUserManager.canAddMoreManagedProfiles(
callingUserId, /* allowedToRemoveOne= */ false)) {
- Slog.i(LOG_TAG, String.format("A work profile already exists."));
+ Slog.i(LOG_TAG, "A work profile already exists.");
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
} finally {
@@ -13722,9 +13734,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
who.flattenToString(), userId));
}
- Slog.i(LOG_TAG, String.format(
- "Marking %s as profile owner on organization-owned device for user %d",
- who.flattenToString(), userId));
+ Slog.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d",
+ who.flattenToString(), userId);
// First, set restriction on removing the profile.
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -14137,7 +14148,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try { // force stop the package before uninstalling
mInjector.getIActivityManager().forceStopPackage(packageName, userId);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+ Slog.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
}
final Uri packageURI = Uri.parse("package:" + packageName);
final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
@@ -14214,10 +14225,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
enforceCallerSystemUserHandle();
- // no effect if it's called from user build
- if (!mInjector.isBuildDebuggable()) {
- return;
- }
final int userId = UserHandle.USER_SYSTEM;
boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
@@ -14397,7 +14404,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
synchronized (getLockObject()) {
if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
- Log.w(LOG_TAG,
+ Slog.w(LOG_TAG,
"Non test-only owner can't be installed with existing accounts.");
return true;
}
@@ -14411,20 +14418,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean compatible = true;
for (Account account : accounts) {
if (hasAccountFeatures(am, account, feature_disallow)) {
- Log.e(LOG_TAG, account + " has " + feature_disallow[0]);
+ Slog.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
compatible = false;
break;
}
if (!hasAccountFeatures(am, account, feature_allow)) {
- Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]);
+ Slog.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
compatible = false;
break;
}
}
if (compatible) {
- Log.w(LOG_TAG, "All accounts are compatible");
+ Slog.w(LOG_TAG, "All accounts are compatible");
} else {
- Log.e(LOG_TAG, "Found incompatible accounts");
+ Slog.e(LOG_TAG, "Found incompatible accounts");
}
return !compatible;
});
@@ -14434,7 +14441,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
return am.hasFeatures(account, features, null, null).getResult();
} catch (Exception e) {
- Log.w(LOG_TAG, "Failed to get account feature", e);
+ Slog.w(LOG_TAG, "Failed to get account feature", e);
return false;
}
}
@@ -14668,24 +14675,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private void sendNetworkLoggingNotificationLocked() {
ensureLocked();
- final ActiveAdmin activeAdmin = getNetworkLoggingControllingAdminLocked();
- if (activeAdmin == null || !activeAdmin.isNetworkLoggingEnabled) {
+ // Send a network logging notification if the admin is a device owner, not profile owner.
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner == null || !deviceOwner.isNetworkLoggingEnabled) {
return;
}
- if (activeAdmin.numNetworkLoggingNotifications
+ if (deviceOwner.numNetworkLoggingNotifications
>= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
return;
}
final long now = System.currentTimeMillis();
- if (now - activeAdmin.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
+ if (now - deviceOwner.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
return;
}
- activeAdmin.numNetworkLoggingNotifications++;
- if (activeAdmin.numNetworkLoggingNotifications
+ deviceOwner.numNetworkLoggingNotifications++;
+ if (deviceOwner.numNetworkLoggingNotifications
>= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
- activeAdmin.lastNetworkLoggingNotificationTimeMs = 0;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
} else {
- activeAdmin.lastNetworkLoggingNotificationTimeMs = now;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
}
final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
@@ -14705,7 +14713,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.bigText(mContext.getString(R.string.network_logging_notification_text)))
.build();
mInjector.getNotificationManager().notify(SystemMessage.NOTE_NETWORK_LOGGING, notification);
- saveSettingsLocked(activeAdmin.getUserHandle().getIdentifier());
+ saveSettingsLocked(deviceOwner.getUserHandle().getIdentifier());
}
/**
@@ -14733,8 +14741,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
0, // flags
targetUserId);
if (info == null || info.serviceInfo == null) {
- Log.e(LOG_TAG, "Fail to look up the service: " + rawIntent
- + " or user " + targetUserId + " is not running");
+ Slog.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent,
+ targetUserId);
return null;
}
if (!expectedPackageName.equals(info.serviceInfo.packageName)) {
@@ -15228,7 +15236,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mInjector.binderWithCleanCallingIdentity(
() -> tm.addDevicePolicyOverrideApn(mContext, apnSetting));
} else {
- Log.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
+ Slog.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
return Telephony.Carriers.INVALID_APN_ID;
}
}
@@ -15252,7 +15260,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mInjector.binderWithCleanCallingIdentity(
() -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting));
} else {
- Log.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
+ Slog.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
return false;
}
}
@@ -15295,7 +15303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mInjector.binderWithCleanCallingIdentity(
() -> tm.getDevicePolicyOverrideApns(mContext));
}
- Log.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
+ Slog.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
return Collections.emptyList();
}
@@ -15448,10 +15456,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
- String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
- if (currentMode == null) {
- currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
- }
+ final String currentMode = ConnectivityManager.getPrivateDnsMode(mContext);
switch (currentMode) {
case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
return PRIVATE_DNS_MODE_OFF;
@@ -15798,8 +15803,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) {
- Log.d(LOG_TAG, String.format("Package %s is not allowed to access cross-profile"
- + "calendar APIs", packageName));
+ Slog.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs",
+ packageName);
return false;
}
final Intent intent = new Intent(
@@ -15813,7 +15818,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId));
} catch (ActivityNotFoundException e) {
- Log.e(LOG_TAG, "View event activity not found", e);
+ Slog.e(LOG_TAG, "View event activity not found", e);
return false;
}
return true;
@@ -15827,7 +15832,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
packageName, UserHandle.getUserId(callingUid));
return packageUid == callingUid;
} catch (NameNotFoundException e) {
- Log.d(LOG_TAG, "Calling package not found", e);
+ Slog.d(LOG_TAG, "Calling package not found", e);
return false;
}
});
@@ -15937,8 +15942,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final long deadline = admin.mProfileOffDeadline;
final int result = makeSuspensionReasons(admin.mSuspendPersonalApps,
deadline != 0 && mInjector.systemCurrentTimeMillis() > deadline);
- Slog.d(LOG_TAG, String.format("getPersonalAppsSuspendedReasons user: %d; result: %d",
- mInjector.userHandleGetCallingUserId(), result));
+ Slog.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d",
+ mInjector.userHandleGetCallingUserId(), result);
return result;
}
}
@@ -16022,9 +16027,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
suspendedExplicitly = profileOwner.mSuspendPersonalApps;
suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED;
- Slog.d(LOG_TAG, String.format(
- "Personal apps suspended explicitly: %b, deadline state: %d",
- suspendedExplicitly, deadlineState));
+ Slog.d(LOG_TAG, "Personal apps suspended explicitly: %b, deadline state: %d",
+ suspendedExplicitly, deadlineState);
final int notificationState =
unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState;
updateProfileOffDeadlineNotificationLocked(
@@ -16125,8 +16129,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (getUserData(userId).mAppsSuspended == suspended) {
return;
}
- Slog.i(LOG_TAG, String.format("%s personal apps for user %d",
- suspended ? "Suspending" : "Unsuspending", userId));
+ Slog.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
+ userId);
if (suspended) {
suspendPersonalAppsInPackageManager(userId);
@@ -16346,8 +16350,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkArgument(!TextUtils.isEmpty(organizationId),
"Enterprise ID may not be empty.");
- Log.i(LOG_TAG,
- String.format("Setting Enterprise ID to %s for user %d", organizationId, userId));
+ Slog.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId);
final String ownerPackage;
synchronized (getLockObject()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 5484a148b0b6..8e31029769d0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -21,6 +21,7 @@ import android.os.ShellCommand;
import com.android.server.devicepolicy.Owners.OwnerDto;
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.List;
import java.util.Objects;
@@ -30,6 +31,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
private static final String CMD_LIST_OWNERS = "list-owners";
+ private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
private final DevicePolicyManagerService mService;
@@ -60,6 +62,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return runSetSafeOperation(pw);
case CMD_LIST_OWNERS:
return runListOwners(pw);
+ case CMD_LIST_POLICY_EXEMPT_APPS:
+ return runListPolicyExemptApps(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -88,6 +92,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
+ " \n\n");
pw.printf(" %s\n", CMD_LIST_OWNERS);
pw.printf(" Lists the device / profile owners per user \n\n");
+ pw.printf(" %s\n", CMD_LIST_POLICY_EXEMPT_APPS);
+ pw.printf(" Lists the apps that are exempt from policies\n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
@@ -119,18 +125,20 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return 0;
}
- private int runListOwners(PrintWriter pw) {
- List<OwnerDto> owners = mService.listAllOwners();
- if (owners.isEmpty()) {
- pw.println("none");
+ private int printAndGetSize(PrintWriter pw, Collection<?> collection, String nameOnSingular) {
+ if (collection.isEmpty()) {
+ pw.printf("no %ss\n", nameOnSingular);
return 0;
}
- int size = owners.size();
- if (size == 1) {
- pw.println("1 owner:");
- } else {
- pw.printf("%d owners:\n", size);
- }
+ int size = collection.size();
+ pw.printf("%d %s%s:\n", size, nameOnSingular, (size == 1 ? "" : "s"));
+ return size;
+ }
+
+ private int runListOwners(PrintWriter pw) {
+ List<OwnerDto> owners = mService.listAllOwners();
+ int size = printAndGetSize(pw, owners, "owner");
+ if (size == 0) return 0;
for (int i = 0; i < size; i++) {
OwnerDto owner = owners.get(i);
@@ -150,4 +158,17 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return 0;
}
+
+ private int runListPolicyExemptApps(PrintWriter pw) {
+ List<String> apps = mService.listPolicyExemptApps();
+ int size = printAndGetSize(pw, apps, "policy exempt app");
+
+ if (size == 0) return 0;
+
+ for (int i = 0; i < size; i++) {
+ String app = apps.get(i);
+ pw.printf(" %d: %s\n", i, app);
+ }
+ return 0;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 457255bd9a73..28a6987bb846 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -68,17 +68,16 @@ public final class FactoryResetter {
IResultReceiver receiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
- Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding",
- mSafetyChecker));
+ Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
try {
factoryResetInternalUnchecked();
} catch (IOException e) {
// Shouldn't happen
- Slog.wtf(TAG, "IOException calling underlying systems", e);
+ Slog.wtf(TAG, e, "IOException calling underlying systems");
}
}
};
- Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker));
+ Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
mSafetyChecker.onFactoryReset(receiver);
return false;
}
@@ -113,9 +112,9 @@ public final class FactoryResetter {
}
private void factoryResetInternalUnchecked() throws IOException {
- Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
- mWipeAdoptableStorage, mWipeFactoryResetProtection));
+ mWipeAdoptableStorage, mWipeFactoryResetProtection);
UserManager um = mContext.getSystemService(UserManager.class);
if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 37dbfc170aff..0b9ece466df4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -18,6 +18,8 @@ package com.android.server.devicepolicy;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -36,6 +38,7 @@ import android.provider.Telephony;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -105,7 +108,9 @@ public final class PersonalAppsSuspensionHelper {
result.remove(pkg);
}
- Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
+ if (Log.isLoggable(LOG_TAG, Log.INFO)) {
+ Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
+ }
return result.toArray(new String[0]);
}
@@ -118,7 +123,7 @@ public final class PersonalAppsSuspensionHelper {
for (final ResolveInfo resolveInfo : matchingActivities) {
if (resolveInfo.activityInfo == null
|| TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
- Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo);
+ Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
continue;
}
final String packageName = resolveInfo.activityInfo.packageName;
@@ -129,7 +134,8 @@ public final class PersonalAppsSuspensionHelper {
result.add(packageName);
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName);
+ Slog.e(LOG_TAG, "Could not find application info for launcher app: %s",
+ packageName);
}
}
return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
index b6420f8ff4bc..19a7659f4d60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
@@ -33,7 +33,7 @@ public interface PolicyUpgraderDataProvider {
* Returns true if the provided {@code userId} is a device owner. May affect some policy
* defaults.
*/
- boolean isUserDeviceOwner(int userId);
+ boolean isDeviceOwner(int userId, ComponentName who);
/**
* Returns true if the storage manager indicates file-based encryption is enabled.
@@ -60,4 +60,9 @@ public interface PolicyUpgraderDataProvider {
* user.
*/
Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId);
+
+ /**
+ * Returns the users to upgrade.
+ */
+ int[] getUsersForUpgrade();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
index cea08634910c..6bc7ba6499ac 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
@@ -62,7 +62,7 @@ public class PolicyVersionUpgrader {
* managed profile user IDs.
* @param dpmsVersion The version to upgrade to.
*/
- public void upgradePolicy(int[] allUsers, int dpmsVersion) {
+ public void upgradePolicy(int dpmsVersion) {
int oldVersion = readVersion();
if (oldVersion >= dpmsVersion) {
Slog.i(LOG_TAG, String.format("Current version %d, latest version %d, not upgrading.",
@@ -70,6 +70,8 @@ public class PolicyVersionUpgrader {
return;
}
+ final int[] allUsers = mProvider.getUsersForUpgrade();
+
//NOTE: The current version is provided in case the XML file format changes in a
// non-backwards-compatible way, so that DeviceAdminData could load it with
// old tags, for example.
@@ -83,6 +85,27 @@ public class PolicyVersionUpgrader {
currentVersion = 1;
}
+ if (currentVersion == 1) {
+ Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
+ // This upgrade step is for Device Owner scenario only: For devices upgrading to S,
+ // if there is a device owner, it retains the ability to control sensors-related
+ // permission grants.
+ for (int userId : allUsers) {
+ DevicePolicyData userData = allUsersData.get(userId);
+ if (userData == null) {
+ continue;
+ }
+ for (ActiveAdmin admin : userData.mAdminList) {
+ if (mProvider.isDeviceOwner(userId, admin.info.getComponent())) {
+ Slog.i(LOG_TAG, String.format(
+ "Marking Device Owner in user %d for permission grant ", userId));
+ admin.mAdminCanGrantSensorsPermissions = true;
+ }
+ }
+ }
+ currentVersion = 2;
+ }
+
writePoliciesAndVersion(allUsers, allUsersData, currentVersion);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 543f3815454e..fa6ef006c61d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEP
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
@@ -58,7 +60,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Class managing bugreport collection upon device owner's request.
*/
public class RemoteBugreportManager {
- private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
@@ -220,7 +221,7 @@ public class RemoteBugreportManager {
mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
} catch (IntentFilter.MalformedMimeTypeException e) {
// should never happen, as setting a constant
- Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e);
+ Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
}
final IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 5ffbd771764d..5140b9f6db58 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -68,6 +68,7 @@ cc_defaults {
"libutils",
"libvold_binder",
"libc++fs",
+ "libziparchive_for_incfs",
],
shared_libs: [
"libandroidfw",
@@ -77,7 +78,6 @@ cc_defaults {
"libincfs",
"liblog",
"libz",
- "libziparchive",
],
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 1fcc2843bd43..db70d44d37f6 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -74,11 +74,21 @@ struct Constants {
// If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
static constexpr auto healthyDataLoaderUptime = 10min;
+
+ // For healthy DLs, we'll retry every ~5secs for ~10min
+ static constexpr auto bindRetryInterval = 5s;
+ static constexpr auto bindGracePeriod = 10min;
+
+ static constexpr auto bindingTimeout = 1min;
+
// 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
static constexpr auto minBindDelay = 10s;
static constexpr auto maxBindDelay = 10000s;
static constexpr auto bindDelayMultiplier = 10;
static constexpr auto bindDelayJitterDivider = 10;
+
+ // Max interval after system invoked the DL when readlog collection can be enabled.
+ static constexpr auto readLogsMaxInterval = 2h;
};
static const Constants& constants() {
@@ -86,6 +96,10 @@ static const Constants& constants() {
return c;
}
+static bool isPageAligned(IncFsSize s) {
+ return (s & (Constants::blockSize - 1)) == 0;
+}
+
template <base::LogSeverity level = base::ERROR>
bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
auto cstr = path::c_str(name);
@@ -283,6 +297,14 @@ void IncrementalService::IncFsMount::cleanupFilesystem(std::string_view root) {
::rmdir(path::c_str(root));
}
+void IncrementalService::IncFsMount::setReadLogsEnabled(bool value) {
+ if (value) {
+ flags |= StorageFlags::ReadLogsEnabled;
+ } else {
+ flags &= ~StorageFlags::ReadLogsEnabled;
+ }
+}
+
IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir)
: mVold(sm.getVoldService()),
mDataLoaderManager(sm.getDataLoaderManager()),
@@ -293,6 +315,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
mTimedQueue(sm.getTimedQueue()),
mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
mFs(sm.getFs()),
+ mClock(sm.getClock()),
mIncrementalDir(rootDir) {
CHECK(mVold) << "Vold service is unavailable";
CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable";
@@ -302,6 +325,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
CHECK(mTimedQueue) << "TimedQueue is unavailable";
CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
CHECK(mFs) << "Fs is unavailable";
+ CHECK(mClock) << "Clock is unavailable";
mJobQueue.reserve(16);
mJobProcessor = std::thread([this]() {
@@ -397,7 +421,7 @@ void IncrementalService::onDump(int fd) {
}
bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
- if (ifs.dataLoaderStub->params().packageName == Constants::systemPackage) {
+ if (ifs.dataLoaderStub->isSystemDataLoader()) {
return true;
}
@@ -649,7 +673,7 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint,
return storageId;
}
-bool IncrementalService::startLoading(StorageId storage,
+bool IncrementalService::startLoading(StorageId storageId,
content::pm::DataLoaderParamsParcel&& dataLoaderParams,
const DataLoaderStatusListener& statusListener,
StorageHealthCheckParams&& healthCheckParams,
@@ -657,12 +681,12 @@ bool IncrementalService::startLoading(StorageId storage,
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
// Per Uid timeouts.
if (!perUidReadTimeouts.empty()) {
- setUidReadTimeouts(storage, perUidReadTimeouts);
+ setUidReadTimeouts(storageId, perUidReadTimeouts);
}
// Re-initialize DataLoader.
std::unique_lock l(mLock);
- const auto ifs = getIfsLocked(storage);
+ const auto ifs = getIfsLocked(storageId);
if (!ifs) {
return false;
}
@@ -677,6 +701,32 @@ bool IncrementalService::startLoading(StorageId storage,
std::move(healthCheckParams), &healthListener);
CHECK(dataLoaderStub);
+ if (dataLoaderStub->isSystemDataLoader()) {
+ // Readlogs from system dataloader (adb) can always be collected.
+ ifs->startLoadingTs = TimePoint::max();
+ } else {
+ // Assign time when installation wants the DL to start streaming.
+ const auto startLoadingTs = mClock->now();
+ ifs->startLoadingTs = startLoadingTs;
+ // Setup a callback to disable the readlogs after max interval.
+ addTimedJob(*mTimedQueue, storageId, Constants::readLogsMaxInterval,
+ [this, storageId, startLoadingTs]() {
+ const auto ifs = getIfs(storageId);
+ if (!ifs) {
+ LOG(WARNING) << "Can't disable the readlogs, invalid storageId: "
+ << storageId;
+ return;
+ }
+ if (ifs->startLoadingTs != startLoadingTs) {
+ LOG(INFO) << "Can't disable the readlogs, timestamp mismatch (new "
+ "installation?): "
+ << storageId;
+ return;
+ }
+ setStorageParams(*ifs, storageId, /*enableReadLogs=*/false);
+ });
+ }
+
return dataLoaderStub->requestStart();
}
@@ -726,11 +776,16 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog
LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId;
return -EINVAL;
}
+ return setStorageParams(*ifs, storageId, enableReadLogs);
+}
- const auto& params = ifs->dataLoaderStub->params();
+int IncrementalService::setStorageParams(IncFsMount& ifs, StorageId storageId,
+ bool enableReadLogs) {
+ const auto& params = ifs.dataLoaderStub->params();
if (enableReadLogs) {
- if (!ifs->readLogsAllowed()) {
- LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId;
+ if (!ifs.readLogsAllowed()) {
+ LOG(ERROR) << "setStorageParams failed, readlogs disallowed for storageId: "
+ << storageId;
return -EPERM;
}
@@ -751,9 +806,19 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog
<< " check failed: " << status.toString8();
return fromBinderStatus(status);
}
+
+ // Check installation time.
+ const auto now = mClock->now();
+ const auto startLoadingTs = ifs.startLoadingTs;
+ if (startLoadingTs <= now && now - startLoadingTs > Constants::readLogsMaxInterval) {
+ LOG(ERROR) << "setStorageParams failed, readlogs can't be enabled at this time, "
+ "storageId: "
+ << storageId;
+ return -EPERM;
+ }
}
- if (auto status = applyStorageParams(*ifs, enableReadLogs); !status.isOk()) {
+ if (auto status = applyStorageParams(ifs, enableReadLogs); !status.isOk()) {
LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
return fromBinderStatus(status);
}
@@ -940,25 +1005,53 @@ std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, St
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
incfs::NewFileParams params, std::span<const uint8_t> data) {
- if (auto ifs = getIfs(storage)) {
- std::string normPath = normalizePathToStorage(*ifs, storage, path);
- if (normPath.empty()) {
- LOG(ERROR) << "Internal error: storageId " << storage
- << " failed to normalize: " << path;
+ const auto ifs = getIfs(storage);
+ if (!ifs) {
+ return -EINVAL;
+ }
+ if (data.size() > params.size) {
+ LOG(ERROR) << "Bad data size - bigger than file size";
+ return -EINVAL;
+ }
+ if (!data.empty() && data.size() != params.size) {
+ // Writing a page is an irreversible operation, and it can't be updated with additional
+ // data later. Check that the last written page is complete, or we may break the file.
+ if (!isPageAligned(data.size())) {
+ LOG(ERROR) << "Bad data size - tried to write half a page?";
return -EINVAL;
}
- if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
- LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
- return err;
+ }
+ const std::string normPath = normalizePathToStorage(*ifs, storage, path);
+ if (normPath.empty()) {
+ LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
+ return -EINVAL;
+ }
+ if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
+ LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
+ return err;
+ }
+ if (params.size > 0) {
+ // Only v2+ incfs supports automatically trimming file over-reserved sizes
+ if (mIncFs->features() & incfs::Features::v2) {
+ if (auto err = mIncFs->reserveSpace(ifs->control, normPath, params.size)) {
+ if (err != -EOPNOTSUPP) {
+ LOG(ERROR) << "Failed to reserve space for a new file: " << err;
+ (void)mIncFs->unlink(ifs->control, normPath);
+ return err;
+ } else {
+ LOG(WARNING) << "Reserving space for backing file isn't supported, "
+ "may run out of disk later";
+ }
+ }
}
if (!data.empty()) {
if (auto err = setFileContent(ifs, id, path, data); err) {
+ (void)mIncFs->unlink(ifs->control, normPath);
return err;
}
}
- return 0;
}
- return -EINVAL;
+ return 0;
}
int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
@@ -1647,7 +1740,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
}
const auto entryUncompressed = entry.method == kCompressStored;
- const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0;
+ const auto entryPageAligned = isPageAligned(entry.offset);
if (!extractNativeLibs) {
// ensure the file is properly aligned and unpacked
@@ -2213,6 +2306,10 @@ sp<content::pm::IDataLoader> IncrementalService::DataLoaderStub::getDataLoader()
return dataloader;
}
+bool IncrementalService::DataLoaderStub::isSystemDataLoader() const {
+ return (params().packageName == Constants::systemPackage);
+}
+
bool IncrementalService::DataLoaderStub::requestCreate() {
return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_CREATED);
}
@@ -2241,17 +2338,44 @@ void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
<< status << " (current " << mCurrentStatus << ")";
}
-Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
+std::optional<Milliseconds> IncrementalService::DataLoaderStub::needToBind() {
std::unique_lock lock(mMutex);
+
+ const auto now = mService.mClock->now();
+ const bool healthy = (mPreviousBindDelay == 0ms);
+
+ if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_BINDING &&
+ now - mCurrentStatusTs <= Constants::bindingTimeout) {
+ LOG(INFO) << "Binding still in progress. "
+ << (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times."
+ : "Already unhealthy, don't do anything.");
+ // Binding still in progress.
+ if (!healthy) {
+ // Already unhealthy, don't do anything.
+ return {};
+ }
+ // The DL is healthy/freshly bound, ok to retry for a few times.
+ if (now - mPreviousBindTs <= Constants::bindGracePeriod) {
+ // Still within grace period.
+ if (now - mCurrentStatusTs >= Constants::bindRetryInterval) {
+ // Retry interval passed, retrying.
+ mCurrentStatusTs = now;
+ mPreviousBindDelay = 0ms;
+ return 0ms;
+ }
+ return {};
+ }
+ // fallthrough, mark as unhealthy, and retry with delay
+ }
+
const auto previousBindTs = mPreviousBindTs;
- const auto now = Clock::now();
mPreviousBindTs = now;
const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
nonCrashingInterval > Constants::healthyDataLoaderUptime) {
mPreviousBindDelay = 0ms;
- return mPreviousBindDelay;
+ return 0ms;
}
constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
@@ -2264,12 +2388,16 @@ Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
-
return mPreviousBindDelay;
}
bool IncrementalService::DataLoaderStub::bind() {
- const auto bindDelay = updateBindDelay();
+ const auto maybeBindDelay = needToBind();
+ if (!maybeBindDelay) {
+ LOG(DEBUG) << "Skipping bind to " << mParams.packageName << " because of pending bind.";
+ return true;
+ }
+ const auto bindDelay = *maybeBindDelay;
if (bindDelay > 1s) {
LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
<< bindDelay.count() / 1000 << "s";
@@ -2279,7 +2407,21 @@ bool IncrementalService::DataLoaderStub::bind() {
auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
this, &result);
if (!status.isOk() || !result) {
- LOG(ERROR) << "Failed to bind a data loader for mount " << id();
+ const bool healthy = (bindDelay == 0ms);
+ LOG(ERROR) << "Failed to bind a data loader for mount " << id()
+ << (healthy ? ", retrying." : "");
+
+ // Internal error, retry for healthy/new DLs.
+ // Let needToBind migrate it to unhealthy after too many retries.
+ if (healthy) {
+ if (mService.addTimedJob(*mService.mTimedQueue, id(), Constants::bindRetryInterval,
+ [this]() { fsmStep(); })) {
+ // Mark as binding so that we know it's not the DL's fault.
+ setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_BINDING);
+ return true;
+ }
+ }
+
return false;
}
return true;
@@ -2339,7 +2481,14 @@ bool IncrementalService::DataLoaderStub::fsmStep() {
// Do nothing, this is a reset state.
break;
case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
- return destroy();
+ switch (currentStatus) {
+ case IDataLoaderStatusListener::DATA_LOADER_BINDING:
+ setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+ return true;
+ default:
+ return destroy();
+ }
+ break;
}
case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
switch (currentStatus) {
@@ -2353,6 +2502,7 @@ bool IncrementalService::DataLoaderStub::fsmStep() {
switch (currentStatus) {
case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+ case IDataLoaderStatusListener::DATA_LOADER_BINDING:
return bind();
case IDataLoaderStatusListener::DATA_LOADER_BOUND:
return create();
@@ -2372,7 +2522,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub");
}
if (id() != mountId) {
- LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+ LOG(ERROR) << "onStatusChanged: mount ID mismatch: expected " << id()
+ << ", but got: " << mountId;
return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
}
if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
@@ -2396,11 +2547,13 @@ void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) {
}
oldStatus = mCurrentStatus;
- mCurrentStatus = newStatus;
targetStatus = mTargetStatus;
-
listener = mStatusListener;
+ // Change the status.
+ mCurrentStatus = newStatus;
+ mCurrentStatusTs = mService.mClock->now();
+
if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
// For unavailable, unbind from DataLoader to ensure proper re-commit.
@@ -2428,7 +2581,8 @@ binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mo
"reportStreamHealth came to invalid DataLoaderStub");
}
if (id() != mountId) {
- LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+ LOG(ERROR) << "reportStreamHealth: mount ID mismatch: expected " << id()
+ << ", but got: " << mountId;
return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
}
{
@@ -2694,6 +2848,8 @@ static std::string toHexString(const RawMetadata& metadata) {
void IncrementalService::DataLoaderStub::onDump(int fd) {
dprintf(fd, " dataLoader: {\n");
dprintf(fd, " currentStatus: %d\n", mCurrentStatus);
+ dprintf(fd, " currentStatusTs: %lldmcs\n",
+ (long long)(elapsedMcs(mCurrentStatusTs, Clock::now())));
dprintf(fd, " targetStatus: %d\n", mTargetStatus);
dprintf(fd, " targetStatusTs: %lldmcs\n",
(long long)(elapsedMcs(mTargetStatusTs, Clock::now())));
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 14e5a7734172..bc441c792084 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -231,6 +231,7 @@ private:
MountId id() const { return mId.load(std::memory_order_relaxed); }
const content::pm::DataLoaderParamsParcel& params() const { return mParams; }
+ bool isSystemDataLoader() const;
void setHealthListener(StorageHealthCheckParams&& healthCheckParams,
const StorageHealthListener* healthListener);
long elapsedMsSinceOldestPendingRead();
@@ -267,7 +268,10 @@ private:
BootClockTsUs getOldestTsFromLastPendingReads();
Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs);
- Milliseconds updateBindDelay();
+ // If the stub has to bind to the DL.
+ // Returns {} if bind operation is already in progress.
+ // Or bind delay in ms.
+ std::optional<Milliseconds> needToBind();
void registerForPendingReads();
void unregisterFromPendingReads();
@@ -283,6 +287,7 @@ private:
std::condition_variable mStatusCondition;
int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
+ TimePoint mCurrentStatusTs = {};
int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
TimePoint mTargetStatusTs = {};
@@ -326,6 +331,7 @@ private:
StorageMap storages;
BindMap bindPoints;
DataLoaderStubPtr dataLoaderStub;
+ TimePoint startLoadingTs = {};
std::atomic<int> nextStorageDirNo{0};
const IncrementalService& incrementalService;
@@ -344,12 +350,7 @@ private:
void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
- void setReadLogsEnabled(bool value) {
- if (value)
- flags |= StorageFlags::ReadLogsEnabled;
- else
- flags &= ~StorageFlags::ReadLogsEnabled;
- }
+ void setReadLogsEnabled(bool value);
int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
static void cleanupFilesystem(std::string_view root);
@@ -407,6 +408,8 @@ private:
IncFsMount::StorageMap::const_iterator storageIt,
std::string_view path) const;
int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
+
+ int setStorageParams(IncFsMount& ifs, StorageId storageId, bool enableReadLogs);
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
@@ -443,6 +446,7 @@ private:
const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
const std::unique_ptr<FsWrapper> mFs;
+ const std::unique_ptr<ClockWrapper> mClock;
const std::string mIncrementalDir;
mutable std::mutex mLock;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index d61328942e5c..2a061226b713 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -209,6 +209,10 @@ public:
ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
}
+ ErrorCode reserveSpace(const Control& control, std::string_view path,
+ IncFsSize size) const final {
+ return incfs::reserveSpace(control, path, size);
+ }
WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
@@ -329,6 +333,14 @@ public:
}
};
+class RealClockWrapper final : public ClockWrapper {
+public:
+ RealClockWrapper() = default;
+ ~RealClockWrapper() = default;
+
+ TimePoint now() const final { return Clock::now(); }
+};
+
RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env)
: mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {}
@@ -388,6 +400,10 @@ std::unique_ptr<FsWrapper> RealServiceManager::getFs() {
return std::make_unique<RealFsWrapper>();
}
+std::unique_ptr<ClockWrapper> RealServiceManager::getClock() {
+ return std::make_unique<RealClockWrapper>();
+}
+
static JavaVM* getJavaVm(JNIEnv* env) {
CHECK(env);
JavaVM* jvm = nullptr;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 245bb3105be5..231b76ff1701 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -107,6 +107,8 @@ public:
virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0;
virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0;
+ virtual ErrorCode reserveSpace(const Control& control, std::string_view path,
+ IncFsSize size) const = 0;
virtual WaitResult waitForPendingReads(
const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
@@ -158,6 +160,12 @@ public:
virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0;
};
+class ClockWrapper {
+public:
+ virtual ~ClockWrapper() = default;
+ virtual TimePoint now() const = 0;
+};
+
class ServiceManagerWrapper {
public:
virtual ~ServiceManagerWrapper() = default;
@@ -170,6 +178,7 @@ public:
virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0;
virtual std::unique_ptr<FsWrapper> getFs() = 0;
+ virtual std::unique_ptr<ClockWrapper> getClock() = 0;
};
// --- Real stuff ---
@@ -187,6 +196,7 @@ public:
std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final;
std::unique_ptr<FsWrapper> getFs() final;
+ std::unique_ptr<ClockWrapper> getClock() final;
private:
template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 5236983c83ff..45b796bf4704 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -248,6 +248,27 @@ public:
}
return binder::Status::ok();
}
+ binder::Status bindToDataLoaderNotOkWithNoDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(bindDelayMs == 0) << bindDelayMs;
+ *_aidl_return = false;
+ return binder::Status::ok();
+ }
+ binder::Status bindToDataLoaderBindingWithNoDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(bindDelayMs == 0) << bindDelayMs;
+ *_aidl_return = true;
+ if (listener) {
+ listener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_BINDING);
+ }
+ return binder::Status::ok();
+ }
binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId,
const DataLoaderParamsParcel& params,
int bindDelayMs,
@@ -351,6 +372,8 @@ public:
MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id));
MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
+ MOCK_CONST_METHOD3(reserveSpace,
+ ErrorCode(const Control& control, std::string_view path, IncFsSize size));
MOCK_CONST_METHOD3(waitForPendingReads,
WaitResult(const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer));
@@ -358,7 +381,10 @@ public:
ErrorCode(const Control& control,
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
- MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); }
+ MockIncFs() {
+ ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());
+ ON_CALL(*this, reserveSpace(_, _, _)).WillByDefault(Return(0));
+ }
void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
@@ -557,6 +583,21 @@ public:
}
};
+class MockClockWrapper : public ClockWrapper {
+public:
+ MOCK_CONST_METHOD0(now, TimePoint());
+
+ void start() { ON_CALL(*this, now()).WillByDefault(Invoke(this, &MockClockWrapper::getClock)); }
+ template <class Delta>
+ void advance(Delta delta) {
+ mClock += delta;
+ }
+
+ TimePoint getClock() const { return mClock; }
+
+ TimePoint mClock = Clock::now();
+};
+
class MockStorageHealthListener : public os::incremental::BnStorageHealthListener {
public:
MOCK_METHOD2(onHealthStatus, binder::Status(int32_t storageId, int32_t status));
@@ -594,7 +635,7 @@ public:
std::unique_ptr<MockLooperWrapper> looper,
std::unique_ptr<MockTimedQueueWrapper> timedQueue,
std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue,
- std::unique_ptr<MockFsWrapper> fs)
+ std::unique_ptr<MockFsWrapper> fs, std::unique_ptr<MockClockWrapper> clock)
: mVold(std::move(vold)),
mDataLoaderManager(std::move(dataLoaderManager)),
mIncFs(std::move(incfs)),
@@ -603,7 +644,8 @@ public:
mLooper(std::move(looper)),
mTimedQueue(std::move(timedQueue)),
mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)),
- mFs(std::move(fs)) {}
+ mFs(std::move(fs)),
+ mClock(std::move(clock)) {}
std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
return std::move(mDataLoaderManager);
@@ -619,6 +661,7 @@ public:
return std::move(mProgressUpdateJobQueue);
}
std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
+ std::unique_ptr<ClockWrapper> getClock() final { return std::move(mClock); }
private:
std::unique_ptr<MockVoldService> mVold;
@@ -630,6 +673,7 @@ private:
std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue;
std::unique_ptr<MockFsWrapper> mFs;
+ std::unique_ptr<MockClockWrapper> mClock;
};
// --- IncrementalServiceTest ---
@@ -657,6 +701,8 @@ public:
mProgressUpdateJobQueue = progressUpdateJobQueue.get();
auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
mFs = fs.get();
+ auto clock = std::make_unique<NiceMock<MockClockWrapper>>();
+ mClock = clock.get();
mIncrementalService = std::make_unique<
IncrementalService>(MockServiceManager(std::move(vold),
std::move(dataloaderManager),
@@ -664,12 +710,13 @@ public:
std::move(jni), std::move(looper),
std::move(timedQueue),
std::move(progressUpdateJobQueue),
- std::move(fs)),
+ std::move(fs), std::move(clock)),
mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
mDataLoaderManager->unbindFromDataLoaderSuccess();
mIncrementalService->onSystemReady();
+ mClock->start();
setupSuccess();
}
@@ -724,6 +771,7 @@ protected:
NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr;
NiceMock<MockFsWrapper>* mFs = nullptr;
+ NiceMock<MockClockWrapper>* mClock = nullptr;
NiceMock<MockDataLoader>* mDataLoader = nullptr;
std::unique_ptr<IncrementalService> mIncrementalService;
TemporaryDir mRootDir;
@@ -853,6 +901,119 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyedAndDelayed) {
mDataLoaderManager->setDataLoaderStatusDestroyed();
}
+TEST_F(IncrementalServiceTest, testDataLoaderOnRestart) {
+ mIncFs->waitForPendingReadsSuccess();
+ mIncFs->openMountSuccess();
+
+ constexpr auto bindRetryInterval = 5s;
+
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(10);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(6);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+
+ // First binds to DataLoader fails... because it's restart.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderNotOkWithNoDelay));
+
+ // Request DL start.
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
+
+ // Retry callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+ auto retryCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Expecting the same bindToDataLoaderNotOkWithNoDelay call.
+ mClock->advance(5s);
+
+ retryCallback();
+ // Retry callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+ retryCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Returning "binding" so that we can retry.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderBindingWithNoDelay));
+
+ // Expecting bindToDataLoaderBindingWithNoDelay call.
+ mClock->advance(5s);
+
+ retryCallback();
+ // No retry callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ // Should not change the bindToDataLoader call count
+ ASSERT_NE(nullptr, mLooper->mCallback);
+ ASSERT_NE(nullptr, mLooper->mCallbackData);
+ auto looperCb = mLooper->mCallback;
+ auto looperCbData = mLooper->mCallbackData;
+ looperCb(-1, -1, looperCbData);
+
+ // Expecting the same bindToDataLoaderBindingWithNoDelay call.
+ mClock->advance(5s);
+
+ // Use pending reads callback to trigger binding.
+ looperCb(-1, -1, looperCbData);
+
+ // No retry callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ // Now we are out of 10m "retry" budget, let's finally bind.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager, &MockDataLoaderManager::bindToDataLoaderOk));
+ mClock->advance(11min);
+
+ // Use pending reads callback to trigger binding.
+ looperCb(-1, -1, looperCbData);
+
+ // No retry callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ // And test the rest of the backoff.
+ // Simulated crash/other connection breakage.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+}
+
TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
mDataLoader->initializeCreateOkNoStatus();
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
@@ -963,7 +1124,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(2);
EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(2);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(4);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(5);
sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>};
NiceMock<MockStorageHealthListener>* listenerMock = listener.get();
@@ -1136,6 +1297,147 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) {
ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
}
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndTimedOut) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto readLogsMaxInterval = 2h;
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Disable readlogs callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval);
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 1hr.
+ mClock->advance(1h);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now call the timed callback, it should turn off the readlogs.
+ callback();
+ // Now advance clock for 2hrs.
+ mClock->advance(readLogsMaxInterval);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+}
+
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndNoTimedOutForSystem) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto readLogsMaxInterval = 2h;
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(0);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
+ // System data loader.
+ mDataLoaderParcel.packageName = "android";
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // No readlogs callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 1hr.
+ mClock->advance(1h);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 2hrs.
+ mClock->advance(readLogsMaxInterval);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), 0);
+}
+
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndNewInstall) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto readLogsMaxInterval = 2h;
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+
+ auto dataLoaderParcel = mDataLoaderParcel;
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(dataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Disable readlogs callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval);
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now advance clock for 1.5hrs.
+ mClock->advance(90min);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+
+ // New installation.
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // New callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, readLogsMaxInterval);
+ auto callback2 = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Old callback should not disable readlogs (setIncFsMountOptions should be called only once).
+ callback();
+ // Advance clock for another 1.5hrs.
+ mClock->advance(90min);
+ // Still success even it's 3hrs past first install.
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+
+ // New one should disable.
+ callback2();
+ // And timeout.
+ mClock->advance(90min);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+}
+
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
mVold->setIncFsMountOptionsSuccess();
mAppOpsManager->checkPermissionSuccess();
@@ -1519,7 +1821,7 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) {
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
@@ -1546,6 +1848,9 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) {
// Empty storage.
mIncFs->countFilledBlocksEmpty();
+ // Mark DataLoader as 'system' so that readlogs don't pollute the timed queue.
+ mDataLoaderParcel.packageName = "android";
+
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5fbf1c4e1f40..6f71e991bb96 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -116,6 +116,7 @@ import com.android.server.camera.CameraServiceProxy;
import com.android.server.clipboard.ClipboardService;
import com.android.server.compat.PlatformCompat;
import com.android.server.compat.PlatformCompatNative;
+import com.android.server.connectivity.PacProxyService;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -389,8 +390,6 @@ public final class SystemServer implements Dumpable {
private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map";
- private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
// maximum number of binder threads used for system_server
// will be higher than the system default
private static final int sMaxBinderThreads = 31;
@@ -1315,6 +1314,7 @@ public final class SystemServer implements Dumpable {
ConsumerIrService consumerIr = null;
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
+ PacProxyService pacProxyService = null;
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
@@ -1325,7 +1325,7 @@ public final class SystemServer implements Dumpable {
false);
boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
- boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+ boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
boolean isWatch = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WATCH);
@@ -1660,8 +1660,7 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
- final boolean hasGsi = SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
- if (hasPdb && !hasGsi) {
+ if (hasPdb) {
t.traceBegin("StartPersistentDataBlock");
mSystemServiceManager.startService(PersistentDataBlockService.class);
t.traceEnd();
@@ -1874,6 +1873,15 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
+ t.traceBegin("StartPacProxyService");
+ try {
+ pacProxyService = new PacProxyService(context);
+ ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService);
+ } catch (Throwable e) {
+ reportWtf("starting PacProxyService", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartConnectivityService");
// This has to be called after NetworkManagementService, NetworkStatsService
// and NetworkPolicyManager because ConnectivityService needs to take these
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
index 87b2c84a30f7..4c5bbebdfd45 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -48,6 +48,8 @@ import com.android.server.infra.AbstractPerUserSystemService;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
/**
* Handles per-user requests received by
@@ -60,6 +62,11 @@ public final class MusicRecognitionManagerPerUserService extends
implements RemoteMusicRecognitionService.Callbacks {
private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName();
+ private static final String MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG =
+ "MusicRecognitionManagerService";
+ private static final String KEY_MUSIC_RECOGNITION_SERVICE_ATTRIBUTION_TAG =
+ "android.media.musicrecognition.attributiontag";
+
// Number of bytes per sample of audio (which is a short).
private static final int BYTES_PER_SAMPLE = 2;
private static final int MAX_STREAMING_SECONDS = 24;
@@ -68,18 +75,24 @@ public final class MusicRecognitionManagerPerUserService extends
@GuardedBy("mLock")
private RemoteMusicRecognitionService mRemoteService;
private final AppOpsManager mAppOpsManager;
+ private final String mAttributionMessage;
- private String mAttributionTag;
- private String mAttributionMessage;
+ // Service info of the remote MusicRecognitionService (which the audio gets forwarded to).
private ServiceInfo mServiceInfo;
+ private CompletableFuture<String> mAttributionTagFuture;
MusicRecognitionManagerPerUserService(
@NonNull MusicRecognitionManagerService primary,
@NonNull Object lock, int userId) {
super(primary, lock, userId);
- mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
+
+ // When attributing audio-access, this establishes that audio access is performed by
+ // MusicRecognitionManager (on behalf of the receiving service, whose attribution tag,
+ // provided by mAttributionTagFuture, is used for the actual calls to startProxyOp(...).
+ mAppOpsManager = getContext().createAttributionContext(
+ MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG).getSystemService(AppOpsManager.class);
mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId);
- mAttributionTag = null;
+ mAttributionTagFuture = null;
mServiceInfo = null;
}
@@ -126,10 +139,13 @@ public final class MusicRecognitionManagerPerUserService extends
new MusicRecognitionServiceCallback(clientCallback),
mMaster.isBindInstantServiceAllowed(),
mMaster.verbose);
+
try {
mServiceInfo =
getContext().getPackageManager().getServiceInfo(
- mRemoteService.getComponentName(), 0);
+ mRemoteService.getComponentName(), PackageManager.GET_META_DATA);
+ mAttributionTagFuture = mRemoteService.getAttributionTag();
+ Slog.i(TAG, "Remote service bound: " + mRemoteService.getComponentName());
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Service was not found.", e);
}
@@ -172,11 +188,13 @@ public final class MusicRecognitionManagerPerUserService extends
ParcelFileDescriptor audioSink = clientPipe.second;
ParcelFileDescriptor clientRead = clientPipe.first;
- mMaster.mExecutorService.execute(() -> {
- streamAudio(recognitionRequest, clientCallback, audioSink);
- });
+ mAttributionTagFuture.thenAcceptAsync(
+ tag -> {
+ streamAudio(tag, recognitionRequest, clientCallback, audioSink);
+ }, mMaster.mExecutorService);
+
// Send the pipe down to the lookup service while we write to it asynchronously.
- mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat());
+ mRemoteService.onAudioStreamStarted(clientRead, recognitionRequest.getAudioFormat());
}
/**
@@ -186,10 +204,12 @@ public final class MusicRecognitionManagerPerUserService extends
* @param clientCallback the callback to notify on errors.
* @param audioSink the sink to which to stream audio to.
*/
- private void streamAudio(@NonNull RecognitionRequest recognitionRequest,
- IMusicRecognitionManagerCallback clientCallback, ParcelFileDescriptor audioSink) {
+ private void streamAudio(@Nullable String attributionTag,
+ @NonNull RecognitionRequest recognitionRequest,
+ IMusicRecognitionManagerCallback clientCallback,
+ ParcelFileDescriptor audioSink) {
try {
- startRecordAudioOp();
+ startRecordAudioOp(attributionTag);
} catch (SecurityException e) {
// A security exception can occur if the MusicRecognitionService (receiving the audio)
// does not (or does no longer) hold the necessary permissions to record audio.
@@ -214,7 +234,7 @@ public final class MusicRecognitionManagerPerUserService extends
Slog.e(TAG, "Audio streaming stopped.", e);
} finally {
audioRecord.release();
- finishRecordAudioOp();
+ finishRecordAudioOp(attributionTag);
try {
clientCallback.onAudioStreamClosed();
} catch (RemoteException ignored) {
@@ -323,23 +343,32 @@ public final class MusicRecognitionManagerPerUserService extends
* Tracks that the RECORD_AUDIO operation started (attributes it to the service receiving the
* audio).
*/
- private void startRecordAudioOp() {
- mAppOpsManager.startProxyOp(
+ private void startRecordAudioOp(@Nullable String attributionTag) {
+ int status = mAppOpsManager.startProxyOp(
Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
mServiceInfo.applicationInfo.uid,
mServiceInfo.packageName,
- mAttributionTag,
+ attributionTag,
mAttributionMessage);
+ // The above should already throw a SecurityException. This is just a fallback.
+ if (status != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException(String.format(
+ "Failed to obtain RECORD_AUDIO permission (status: %d) for "
+ + "receiving service: %s", status, mServiceInfo.getComponentName()));
+ }
+ Slog.i(TAG, String.format(
+ "Starting audio streaming. Attributing to %s (%d) with tag '%s'",
+ mServiceInfo.packageName, mServiceInfo.applicationInfo.uid, attributionTag));
}
/** Tracks that the RECORD_AUDIO operation finished. */
- private void finishRecordAudioOp() {
+ private void finishRecordAudioOp(@Nullable String attributionTag) {
mAppOpsManager.finishProxyOp(
Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
mServiceInfo.applicationInfo.uid,
mServiceInfo.packageName,
- mAttributionTag);
+ attributionTag);
}
/** Establishes an audio stream from the DSP audio source. */
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
index 6c7d673ffe11..99b448211492 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
@@ -20,15 +20,20 @@ import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.media.AudioFormat;
+import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback;
import android.media.musicrecognition.IMusicRecognitionService;
import android.media.musicrecognition.MusicRecognitionService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.text.format.DateUtils;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback;
+import java.util.concurrent.CompletableFuture;
+
+
/** Remote connection to an instance of {@link MusicRecognitionService}. */
public class RemoteMusicRecognitionService extends
AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService,
@@ -81,9 +86,26 @@ public class RemoteMusicRecognitionService extends
* Sends the given descriptor to the app's {@link MusicRecognitionService} to read the
* audio.
*/
- public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd,
+ public void onAudioStreamStarted(@NonNull ParcelFileDescriptor fd,
@NonNull AudioFormat audioFormat) {
scheduleAsyncRequest(
binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback));
}
+
+
+ /**
+ * Returns the name of the <attribution> tag defined in the remote service's manifest.
+ */
+ public CompletableFuture<String> getAttributionTag() {
+ CompletableFuture<String> attributionTagFuture = new CompletableFuture<String>();
+ scheduleAsyncRequest(
+ binder -> binder.getAttributionTag(
+ new IMusicRecognitionAttributionTagCallback.Stub() {
+ @Override
+ public void onAttributionTag(String tag) throws RemoteException {
+ attributionTagFuture.complete(tag);
+ }
+ }));
+ return attributionTagFuture;
+ }
}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
index 169b85eb7e06..b07fe19393b2 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.smartspace;
import static android.Manifest.permission.MANAGE_SMARTSPACE;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.content.Context.SMARTSPACE_SERVICE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -161,11 +162,13 @@ public class SmartspaceManagerService extends
Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
- if (!(mServiceNameResolver.isTemporary(userId)
+ Context ctx = getContext();
+ if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED
+ || mServiceNameResolver.isTemporary(userId)
|| mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
- String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid();
+ String msg = "Permission Denial: Cannot call " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 334e53a3aec7..988c02bfb3db 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -23,14 +23,17 @@ package {
android_test {
name: "PackageManagerServiceUnitTests",
- srcs: ["src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
"androidx.test.rules",
"androidx.test.runner",
"junit",
+ "kotlin-test",
"services.core",
"servicestests-utils",
- "testng",
"truth-prebuilt",
],
platform_apis: true,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 0fa9a1def381..d5eda203e42f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -25,8 +25,8 @@ import android.os.PatternMatcher
import android.util.ArraySet
import com.android.server.SystemConfig
import com.android.server.compat.PlatformCompat
-import com.android.server.pm.verify.domain.DomainVerificationCollector
import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationCollector
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
index 8ef92393242a..9693f3bab127 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -19,6 +19,7 @@ package com.android.server.pm.test.verify.domain
import android.content.pm.verify.domain.DomainSet
import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationState
import android.content.pm.verify.domain.DomainVerificationUserState
import android.os.Parcel
import android.os.Parcelable
@@ -74,7 +75,9 @@ class DomainVerificationCoreApiTest {
DomainVerificationInfo(
UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
"com.test.pkg",
- massiveSet.withIndex().associate { it.value to it.index }
+ massiveSet.withIndex().associate {
+ it.value to DomainVerificationState.convertToInfoState(it.index)
+ }
)
},
unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 53f0ca20e787..7e25901301aa 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -20,20 +20,21 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageUserState
-import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationEnforcer
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
-import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.spyThrowOnUnmocked
import com.android.server.testutils.whenever
@@ -50,6 +51,8 @@ import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.test.assertFailsWith
+import kotlin.test.fail
@RunWith(Parameterized::class)
class DomainVerificationEnforcerTest {
@@ -81,47 +84,49 @@ class DomainVerificationEnforcerTest {
whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
true
}
+ whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
})
}
}
- val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger, DomainVerificationService> =
- {
- val callingUidInt = AtomicInteger(-1)
- val callingUserIdInt = AtomicInteger(-1)
-
- val connection: DomainVerificationManagerInternal.Connection =
- mockThrowOnUnmocked {
- whenever(callingUid) { callingUidInt.get() }
- whenever(callingUserId) { callingUserIdInt.get() }
- whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
- whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
- whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
- whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
- whenever(schedule(anyInt(), any()))
- whenever(scheduleWriteSettings())
- whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
- whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
- true
- }
+ val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger,
+ DomainVerificationService> = {
+ val callingUidInt = AtomicInteger(-1)
+ val callingUserIdInt = AtomicInteger(-1)
+
+ val connection: DomainVerificationManagerInternal.Connection =
+ mockThrowOnUnmocked {
+ whenever(callingUid) { callingUidInt.get() }
+ whenever(callingUserId) { callingUserIdInt.get() }
+ whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
+ whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
+ whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
+ whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
+ whenever(schedule(anyInt(), any()))
+ whenever(scheduleWriteSettings())
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
}
- val service = DomainVerificationService(
- it,
- mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
- mockThrowOnUnmocked {
- whenever(
- isChangeEnabled(
- anyLong(),
- any()
- )
- ) { true }
- }).apply {
- setConnection(connection)
+ whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
}
-
- Triple(callingUidInt, callingUserIdInt, service)
+ val service = DomainVerificationService(
+ it,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(
+ isChangeEnabled(
+ anyLong(),
+ any()
+ )
+ ) { true }
+ }).apply {
+ setConnection(connection)
}
+ Triple(callingUidInt, callingUserIdInt, service)
+ }
+
fun enforcer(
type: Type,
name: String,
@@ -175,7 +180,7 @@ class DomainVerificationEnforcerTest {
service(Type.INTERNAL, "setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
it.targetPackageName,
- DomainVerificationManager.STATE_SUCCESS,
+ DomainVerificationState.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
@@ -206,7 +211,7 @@ class DomainVerificationEnforcerTest {
setDomainVerificationStatus(
it.targetDomainSetId,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service(Type.VERIFIER, "setStatusInternalUid") {
@@ -214,7 +219,7 @@ class DomainVerificationEnforcerTest {
it.callingUid,
it.targetDomainSetId,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
@@ -475,24 +480,7 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// User selector makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
-
- // User selector doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
}
val callingUserId = 0
@@ -529,24 +517,7 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// User selector makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
-
- // User selector doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
}
val callingUserId = 0
@@ -590,24 +561,10 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// Legacy makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
-
- // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ // The legacy selector does a silent failure when the user IDs don't match, so it
+ // cannot verify the non-existent user ID check, as it will not throw an Exception.
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+ verifyUserIdCheck = false)
}
val callingUserId = 0
@@ -642,27 +599,29 @@ class DomainVerificationEnforcerTest {
}
val target = params.construct(context)
- fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
- // Legacy makes no distinction by UID
- val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
+ // Legacy code can return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ // as an error code. This is distinct from the class level assertFails as unfortunately
+ // the same number, 0, is used in opposite contexts, where it does represent a failure
+ // for this legacy case, but not for the modern APIs.
+ fun assertFailsLegacy(block: () -> Any?) {
+ try {
+ val value = block()
+ if ((value as? Int)
+ != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ ) {
+ throw AssertionError("Expected call to return false, was $value")
}
+ } catch (e: SecurityException) {
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Any of these 2 exceptions are considered failures, which is expected
}
+ }
- // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // Legacy makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+ assertFailsMethod = ::assertFailsLegacy)
}
val callingUserId = 0
@@ -704,17 +663,8 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// Owner querent makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+ verifyInvisiblePkg = false)
}
val callingUserId = 0
@@ -782,22 +732,88 @@ class DomainVerificationEnforcerTest {
return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
}
+ private fun runCrossUserMethod(
+ allUids: Iterable<Int>,
+ target: Any,
+ callingUserId: Int,
+ targetUserId: Int,
+ throws: Boolean,
+ verifyUserIdCheck: Boolean = true,
+ verifyInvisiblePkg: Boolean = true,
+ assertFailsMethod: (() -> Any?) -> Unit = ::assertFails,
+ ) {
+ if (throws) {
+ allUids.forEach {
+ assertFailsMethod {
+ // When testing a non-user ID failure, send an invalid user ID.
+ // This ensures the failure occurs before the user ID check is run.
+ try {
+ runMethod(target, it, visible = true, callingUserId, 100)
+ } catch (e: SecurityException) {
+ if (verifyUserIdCheck) {
+ e.message?.let {
+ if (it.contains("user ID", ignoreCase = true)
+ || it.contains("100")) {
+ fail(
+ "Method should not check user existence before permissions"
+ )
+ }
+ }
+ }
+
+ // Rethrow to allow normal fail checking logic to run
+ throw e
+ }
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ if (verifyInvisiblePkg) {
+ allUids.forEach {
+ assertFailsMethod {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
+ }
+ }
+
+ if (verifyUserIdCheck) {
+ // An invalid target user ID should always fail
+ allUids.forEach {
+ assertFailsWith(SecurityException::class) {
+ runMethod(target, it, visible = true, callingUserId, 100)
+ }
+ }
+
+ // An invalid calling user ID should always fail, although this cannot happen in prod
+ allUids.forEach {
+ assertFailsWith(SecurityException::class) {
+ runMethod(target, it, visible = true, 100, targetUserId)
+ }
+ }
+ }
+ }
+
private fun assertFails(block: () -> Any?) {
try {
val value = block()
- // Some methods return false rather than throwing, so check that as well
- if ((value as? Boolean) != false) {
- // Can also return default value if it's a legacy call
- if ((value as? Int)
- != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
- ) {
- throw AssertionError("Expected call to return false, was $value")
- }
+ // Some methods return false or an error rather than throwing, so check that as well
+ val valueAsBoolean = value as? Boolean
+ if (valueAsBoolean == false) {
+ // Expected failure, do not throw
+ return
+ }
+
+ val valueAsInt = value as? Int
+ if (valueAsInt != null && valueAsInt == DomainVerificationManager.STATUS_OK) {
+ throw AssertionError("Expected call to return false, was $value")
}
} catch (e: SecurityException) {
} catch (e: PackageManager.NameNotFoundException) {
- } catch (e: DomainVerificationManager.InvalidDomainSetException) {
- // Any of these 3 exceptions are considered failures, which is expected
+ // Any of these 2 exceptions are considered failures, which is expected
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
new file mode 100644
index 000000000000..8ae4c5ae96a3
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+
+import com.android.server.pm.verify.domain.DomainVerificationService;
+
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Proxies Kotlin calls to the Java layer such that null values can be passed for {@link NonNull}
+ * marked parameters, as Kotlin disallows this at the compiler leveling, preventing the null error
+ * codes from being tested.
+ */
+class DomainVerificationJavaUtil {
+
+ static int setStatusForceNullable(@NonNull DomainVerificationService service,
+ @Nullable UUID domainSetId, @Nullable Set<String> domains, int state)
+ throws PackageManager.NameNotFoundException {
+ return service.setDomainVerificationStatus(domainSetId, domains, state);
+ }
+
+ static int setUserSelectionForceNullable(@NonNull DomainVerificationService service,
+ @Nullable UUID domainSetId, @Nullable Set<String> domains, boolean enabled,
+ @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ return service.setDomainVerificationUserSelection(domainSetId, domains, enabled, userId);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
index 9a3bd994eac0..509824d93512 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
@@ -19,9 +19,9 @@ package com.android.server.pm.test.verify.domain
import android.content.pm.IntentFilterVerificationInfo
import android.content.pm.PackageManager
import android.util.ArraySet
-import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.readXml
import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.writeXml
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Rule
import org.junit.Test
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
new file mode 100644
index 000000000000..0e74b65d25d5
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationUserState
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+import kotlin.test.assertFailsWith
+
+class DomainVerificationManagerApiTest {
+
+ companion object {
+ private const val PKG_ONE = "com.test.one"
+ private const val PKG_TWO = "com.test.two"
+ private const val PKG_THREE = "com.test.three"
+
+ private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+ private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+ private val UUID_THREE = UUID.fromString("0b3260ed-07c4-4b45-840b-237f8fb8b433")
+ private val UUID_INVALID = UUID.fromString("ad33babc-490b-4965-9d78-7e91248b00f")
+
+ private val DOMAIN_BASE = DomainVerificationManagerApiTest::class.java.packageName
+ private val DOMAIN_1 = "one.$DOMAIN_BASE"
+ private val DOMAIN_2 = "two.$DOMAIN_BASE"
+ private val DOMAIN_3 = "three.$DOMAIN_BASE"
+ private val DOMAIN_4 = "four.$DOMAIN_BASE"
+ }
+
+ @Test
+ fun queryValidVerificationPackageNames() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkgWithDomains.getName())
+ }
+
+ @Test
+ fun getDomainVerificationInfoId() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ assertThat(service.getDomainVerificationInfoId(PKG_ONE)).isEqualTo(UUID_ONE)
+ assertThat(service.getDomainVerificationInfoId(PKG_TWO)).isEqualTo(UUID_TWO)
+
+ assertThat(service.getDomainVerificationInfoId("invalid.pkg.name")).isEqualTo(null)
+ }
+
+ @Test
+ fun getDomainVerificationInfo() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ val infoOne = service.getDomainVerificationInfo(pkgWithDomains.getName())
+ assertThat(infoOne).isNotNull()
+ assertThat(infoOne!!.identifier).isEqualTo(pkgWithDomains.domainSetId)
+ assertThat(infoOne.packageName).isEqualTo(pkgWithDomains.getName())
+ assertThat(infoOne.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DomainVerificationInfo.STATE_NO_RESPONSE,
+ DOMAIN_2 to DomainVerificationInfo.STATE_NO_RESPONSE,
+ ))
+
+ assertThat(service.getDomainVerificationInfo(pkgWithoutDomains.getName())).isNull()
+
+ assertFailsWith(PackageManager.NameNotFoundException::class) {
+ service.getDomainVerificationInfo("invalid.pkg.name")
+ }
+ }
+
+ @Test
+ fun setStatus() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+
+ val map = mutableMapOf(pkg1.getName() to pkg1, pkg2.getName() to pkg2)
+ val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_2), 1100))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setStatus(UUID_INVALID, setOf(DOMAIN_1), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID)
+
+ assertThat(DomainVerificationJavaUtil.setStatusForceNullable(service, null,
+ setOf(DOMAIN_1), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL)
+
+ assertThat(DomainVerificationJavaUtil.setStatusForceNullable(service, UUID_ONE, null,
+ 1100)).isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setStatus(UUID_ONE, emptySet(), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_3), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_2, DOMAIN_3), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), 15))
+ .isEqualTo(DomainVerificationManager.ERROR_INVALID_STATE_CODE)
+
+ map.clear()
+ assertFailsWith(PackageManager.NameNotFoundException::class){
+ service.setStatus(UUID_ONE, setOf(DOMAIN_1), 1100)
+ }
+ }
+
+ @Test
+ fun setDomainVerificationLinkHandlingAllowed() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+
+ val map = mutableMapOf(pkg1.getName() to pkg1, pkg2.getName() to pkg2)
+ val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
+
+ service.setDomainVerificationLinkHandlingAllowed(PKG_ONE, false, 0);
+
+ // Should edit same package, same user
+ assertThat(service.getDomainVerificationUserState(PKG_ONE, 0)
+ ?.isLinkHandlingAllowed).isEqualTo(false)
+
+ // Shouldn't edit different user
+ assertThat(service.getDomainVerificationUserState(PKG_ONE, 1)
+ ?.isLinkHandlingAllowed).isEqualTo(true)
+
+ // Shouldn't edit different package
+ assertThat(service.getDomainVerificationUserState(PKG_TWO, 0)
+ ?.isLinkHandlingAllowed).isEqualTo(true)
+
+ assertFailsWith(PackageManager.NameNotFoundException::class){
+ service.setDomainVerificationLinkHandlingAllowed("invalid.pkg.name", false, 0);
+ }
+ }
+
+ @Test
+ fun setUserSelection() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+ val pkg3 = mockPkgSetting(PKG_THREE, UUID_THREE, listOf(DOMAIN_1, DOMAIN_2))
+
+ val map = mutableMapOf(
+ pkg1.getName() to pkg1,
+ pkg2.getName() to pkg2,
+ pkg3.getName() to pkg3
+ )
+ val service = makeService(map::get).apply { addPackages(pkg1, pkg2, pkg3) }
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, 0))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_INVALID, setOf(DOMAIN_1), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID)
+
+ assertThat(DomainVerificationJavaUtil.setUserSelectionForceNullable(service, null,
+ setOf(DOMAIN_1), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL)
+
+ assertThat(DomainVerificationJavaUtil.setUserSelectionForceNullable(service, UUID_ONE, null,
+ true, 0)).isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setUserSelection(UUID_ONE, emptySet(), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_3), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_1, DOMAIN_2, DOMAIN_3), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ service.setStatus(UUID_ONE, setOf(DOMAIN_2), DomainVerificationInfo.STATE_SUCCESS)
+
+ assertThat(service.setUserSelection(UUID_THREE, setOf(DOMAIN_2), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_UNABLE_TO_APPROVE)
+
+ map.clear()
+ assertFailsWith(PackageManager.NameNotFoundException::class){
+ service.setUserSelection(UUID_ONE, setOf(DOMAIN_1), true, 0)
+ }
+ }
+
+ @Test
+ fun getDomainVerificationUserState() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ val infoOne = service.getDomainVerificationUserState(pkgWithDomains.getName(), 0)
+ assertThat(infoOne).isNotNull()
+ assertThat(infoOne!!.identifier).isEqualTo(pkgWithDomains.domainSetId)
+ assertThat(infoOne.packageName).isEqualTo(pkgWithDomains.getName())
+ assertThat(infoOne.isLinkHandlingAllowed).isTrue()
+ assertThat(infoOne.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DomainVerificationUserState.DOMAIN_STATE_NONE,
+ DOMAIN_2 to DomainVerificationUserState.DOMAIN_STATE_NONE,
+ ))
+
+ val infoTwo = service.getDomainVerificationUserState(pkgWithoutDomains.getName(), 0)
+ assertThat(infoTwo).isNotNull()
+ assertThat(infoTwo!!.identifier).isEqualTo(pkgWithoutDomains.domainSetId)
+ assertThat(infoTwo.packageName).isEqualTo(pkgWithoutDomains.getName())
+ assertThat(infoOne.isLinkHandlingAllowed).isTrue()
+ assertThat(infoTwo.hostToStateMap).isEmpty()
+
+ assertFailsWith(PackageManager.NameNotFoundException::class) {
+ service.getDomainVerificationUserState("invalid.pkg.name", 0)
+ }
+ }
+
+ @Test
+ fun getOwnersForDomain() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+ val service = makeService(pkg1, pkg2).apply {
+ addPackages(pkg1, pkg2)
+ }
+
+ assertThat(service.getOwnersForDomain(DOMAIN_1, 0)).isEmpty()
+
+ service.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
+
+ service.setStatus(pkg2.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
+
+ service.setUserSelection(pkg1.domainSetId, setOf(DOMAIN_2), true, 0)
+
+ service.getOwnersForDomain(DOMAIN_1, 0).let {
+ assertThat(it).hasSize(2)
+ assertThat(it[0].packageName).isEqualTo(pkg1.getName())
+ assertThat(it[0].isOverrideable).isEqualTo(false)
+ assertThat(it[1].packageName).isEqualTo(pkg2.getName())
+ assertThat(it[1].isOverrideable).isEqualTo(false)
+ }
+
+ service.getOwnersForDomain(DOMAIN_2, 0).let {
+ assertThat(it).hasSize(1)
+ assertThat(it.single().packageName).isEqualTo(pkg1.getName())
+ assertThat(it.single().isOverrideable).isEqualTo(true)
+ }
+ assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
+
+ service.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
+ service.getOwnersForDomain(DOMAIN_2, 0).let {
+ assertThat(it).hasSize(1)
+ assertThat(it.single().packageName).isEqualTo(pkg2.getName())
+ assertThat(it.single().isOverrideable).isEqualTo(true)
+ }
+ assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
+ }
+
+ private fun makeService(vararg pkgSettings: PackageSetting) =
+ makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } }
+
+ private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+ DomainVerificationService(mockThrowOnUnmocked {
+ // Assume the test has every permission necessary
+ whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+ whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+ PackageManager.PERMISSION_GRANTED
+ }
+ }, mockThrowOnUnmocked {
+ whenever(linkedApps) { ArraySet<String>() }
+ }, mockThrowOnUnmocked {
+ whenever(isChangeEnabled(anyLong(), any())) { true }
+ }).apply {
+ setConnection(mockThrowOnUnmocked {
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
+ whenever(scheduleWriteSettings())
+
+ // Need to provide an internal UID so some permission checks are ignored
+ whenever(callingUid) { Process.ROOT_UID }
+ whenever(callingUserId) { 0 }
+
+ whenever(getPackageSettingLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)
+ }
+ whenever(getPackageLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)?.getPkg()
+ }
+ })
+ }
+
+ private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
+ DOMAIN_1, DOMAIN_2
+ )) = mockThrowOnUnmocked<PackageSetting> {
+ val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { pkgName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+ val activityList = listOf(
+ ParsedActivity().apply {
+ domains.forEach {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(it, null)
+ }
+ )
+ }
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+
+ whenever(getPkg()) { pkg }
+ whenever(getName()) { pkgName }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(getInstantApp(anyInt())) { false }
+ whenever(firstInstallTime) { 0L }
+ }
+
+ fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
+ pkgSettings.forEach(::addPackage)
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
index 8c31c65e1b0a..a5db3c578670 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -16,8 +16,8 @@
package com.android.server.pm.test.verify.domain
-import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainVerificationUserState
import com.android.server.pm.verify.domain.DomainVerificationPersistence
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
new file mode 100644
index 000000000000..fe3672d06bc0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_UNMODIFIABLE
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import android.util.Xml
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+
+class DomainVerificationPackageTest {
+
+ companion object {
+ private const val PKG_ONE = "com.test.one"
+ private const val PKG_TWO = "com.test.two"
+ private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+ private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+
+ private val DOMAIN_BASE = DomainVerificationPackageTest::class.java.packageName
+ private val DOMAIN_1 = "one.$DOMAIN_BASE"
+ private val DOMAIN_2 = "two.$DOMAIN_BASE"
+ private val DOMAIN_3 = "three.$DOMAIN_BASE"
+ private val DOMAIN_4 = "four.$DOMAIN_BASE"
+
+ private const val USER_ID = 0
+ }
+
+ private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
+ private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
+
+ @Test
+ fun addPackageFirstTime() {
+ val service = makeService(pkg1, pkg2)
+ service.addPackage(pkg1)
+ val info = service.getInfo(pkg1.getName())
+ assertThat(info.packageName).isEqualTo(pkg1.getName())
+ assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+
+ val userState = service.getUserState(pkg1.getName())
+ assertThat(userState.packageName).isEqualTo(pkg1.getName())
+ assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
+ assertThat(userState.user.identifier).isEqualTo(USER_ID)
+ assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkg1.getName())
+ }
+
+ @Test
+ fun addPackageActive() {
+ // language=XML
+ val xml = """
+ <?xml?>
+ <domain-verifications>
+ <active>
+ <package-state
+ packageName="${pkg1.getName()}"
+ id="${pkg1.domainSetId}"
+ >
+ <state>
+ <domain name="$DOMAIN_1" state="$STATE_SUCCESS"/>
+ </state>
+ <user-states>
+ <user-state userId="$USER_ID" allowLinkHandling="false">
+ <enabled-hosts>
+ <host name="$DOMAIN_2"/>
+ </enabled-hosts>
+ </user-state>
+ </user-states>
+ </package-state>
+ </active>
+ </domain-verifications>
+ """.trimIndent()
+
+ val service = makeService(pkg1, pkg2)
+ xml.byteInputStream().use {
+ service.readSettings(Xml.resolvePullParser(it))
+ }
+
+ service.addPackage(pkg1)
+
+ val info = service.getInfo(pkg1.getName())
+ assertThat(info.packageName).isEqualTo(pkg1.getName())
+ assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+
+ val userState = service.getUserState(pkg1.getName())
+ assertThat(userState.packageName).isEqualTo(pkg1.getName())
+ assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
+ assertThat(userState.user.identifier).isEqualTo(USER_ID)
+ assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkg1.getName())
+ }
+
+ @Test
+ fun migratePackageDropDomain() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
+ listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
+ listOf(DOMAIN_1, DOMAIN_2))
+
+ // Test 4 domains:
+ // 1 will be approved and preserved, 2 will be selected and preserved,
+ // 3 will be denied and dropped, 4 will be selected and dropped
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ // To test the approve/denial states, use the internal methods for this variant
+ service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_APPROVED,
+ ArraySet(setOf(DOMAIN_1)))
+ service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_DENIED,
+ ArraySet(setOf(DOMAIN_3)))
+ service.setUserSelection(
+ UUID_ONE, setOf(DOMAIN_2, DOMAIN_4), true, USER_ID)
+
+ // Check the verifier cannot change the shell approve/deny states
+ service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_3), STATE_SUCCESS)
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_UNMODIFIABLE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ DOMAIN_3 to STATE_UNMODIFIABLE,
+ DOMAIN_4 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ DOMAIN_3 to DOMAIN_STATE_NONE,
+ DOMAIN_4 to DOMAIN_STATE_SELECTED,
+ ))
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ map[pkgName] = pkgAfter
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_UNMODIFIABLE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
+ fun migratePackageDropAll() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, emptyList())
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
+ assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
+ assertThat(service.queryValidVerificationPackageNames()).isEmpty()
+ }
+
+ @Test
+ fun migratePackageAddDomain() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
+ listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
+ listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
+
+ // Test 3 domains:
+ // 1 will be verified and preserved, 2 will be selected and preserved,
+ // 3 will be new and default
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS)
+ service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID)
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ DOMAIN_3 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ DOMAIN_3 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
+ fun migratePackageAddAll() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, emptyList())
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
+ assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
+ assertThat(service.queryValidVerificationPackageNames()).isEmpty()
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ private fun DomainVerificationService.getInfo(pkgName: String) =
+ getDomainVerificationInfo(pkgName)
+ .also { assertThat(it).isNotNull() }!!
+
+ private fun DomainVerificationService.getUserState(pkgName: String) =
+ getDomainVerificationUserState(pkgName, USER_ID)
+ .also { assertThat(it).isNotNull() }!!
+
+ private fun makeService(vararg pkgSettings: PackageSetting) =
+ makeService { pkgName -> pkgSettings.find { pkgName == it.getName()} }
+
+ private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+ DomainVerificationService(mockThrowOnUnmocked {
+ // Assume the test has every permission necessary
+ whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+ whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+ PackageManager.PERMISSION_GRANTED
+ }
+ }, mockThrowOnUnmocked {
+ whenever(linkedApps) { ArraySet<String>() }
+ }, mockThrowOnUnmocked {
+ whenever(isChangeEnabled(ArgumentMatchers.anyLong(), any())) { true }
+ }).apply {
+ setConnection(mockThrowOnUnmocked {
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
+ whenever(scheduleWriteSettings())
+
+ // Need to provide an internal UID so some permission checks are ignored
+ whenever(callingUid) { Process.ROOT_UID }
+ whenever(callingUserId) { 0 }
+
+ whenever(getPackageSettingLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)!!
+ }
+ whenever(getPackageLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)!!.getPkg()
+ }
+ })
+ }
+
+ private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
+ DOMAIN_1, DOMAIN_2
+ )) = mockThrowOnUnmocked<PackageSetting> {
+ val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { pkgName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+ val activityList = listOf(
+ ParsedActivity().apply {
+ domains.forEach {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(it, null)
+ }
+ )
+ }
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+
+ whenever(getPkg()) { pkg }
+ whenever(getName()) { pkgName }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(getInstantApp(anyInt())) { false }
+ whenever(firstInstallTime) { 0L }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
index 6597577cf14f..f8fda12f806d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.verify.domain
-import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
import android.util.ArrayMap
import android.util.TypedXmlPullParser
import android.util.TypedXmlSerializer
@@ -117,11 +117,11 @@ class DomainVerificationPersistenceTest {
@Test
fun readMalformed() {
val stateZero = mockEmptyPkgState(0).apply {
- stateMap["example.com"] = DomainVerificationManager.STATE_SUCCESS
- stateMap["example.org"] = DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED
+ stateMap["example.com"] = DomainVerificationState.STATE_SUCCESS
+ stateMap["example.org"] = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED
// A domain without a written state falls back to default
- stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
+ stateMap["missing-state.com"] = DomainVerificationState.STATE_NO_RESPONSE
userStates[1] = DomainVerificationInternalUserState(1).apply {
addHosts(setOf("example-user1.com", "example-user1.org"))
@@ -159,9 +159,9 @@ class DomainVerificationPersistenceTest {
>
<state>
<domain name="example.com" state="${
- DomainVerificationManager.STATE_SUCCESS}"/>
+ DomainVerificationState.STATE_SUCCESS}"/>
<domain name="example.org" state="${
- DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
+ DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED}"/>
<not-domain name="not-domain.com" state="1"/>
<domain name="missing-state.com"/>
</state>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
index 91e5beccee09..a9b77ea6d95b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
@@ -21,20 +21,20 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
+import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationRequest
-import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationState
import android.os.Bundle
import android.os.UserHandle
import android.util.ArraySet
import com.android.server.DeviceIdleInternal
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationCollector
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2
-import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
@@ -106,20 +106,22 @@ class DomainVerificationProxyTest {
when (val pkgName = arguments[0] as String) {
TEST_PKG_NAME_TARGET_ONE -> DomainVerificationInfo(
TEST_UUID_ONE, pkgName, mapOf(
- "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE,
- "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE
+ "example1.com" to DomainVerificationInfo.STATE_NO_RESPONSE,
+ "example2.com" to DomainVerificationInfo.STATE_NO_RESPONSE
)
)
TEST_PKG_NAME_TARGET_TWO -> DomainVerificationInfo(
TEST_UUID_TWO, pkgName, mapOf(
- "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE,
- "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE
+ "example3.com" to DomainVerificationInfo.STATE_NO_RESPONSE,
+ "example4.com" to DomainVerificationInfo.STATE_NO_RESPONSE
)
)
else -> throw IllegalArgumentException("Unexpected package name $pkgName")
}
}
- whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt()))
+ whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt())) {
+ DomainVerificationManager.STATUS_OK
+ }
}
collector = mockThrowOnUnmocked {
whenever(collectValidAutoVerifyDomains(any())) {
@@ -316,7 +318,7 @@ class DomainVerificationProxyTest {
eq(TEST_CALLING_UID_ACCEPT),
idCaptor.capture(),
hostCaptor.capture(),
- eq(DomainVerificationManager.STATE_SUCCESS)
+ eq(DomainVerificationState.STATE_SUCCESS)
)
assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt
new file mode 100644
index 000000000000..6859b113298d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.annotation.UserIdInt
+import com.android.server.pm.verify.domain.DomainVerificationService
+import java.util.UUID
+
+fun DomainVerificationService.setStatus(domainSetId: UUID, domains: Set<String>, state: Int) =
+ setDomainVerificationStatus(domainSetId, domains.toMutableSet(), state)
+
+fun DomainVerificationService.setUserSelection(
+ domainSetId: UUID,
+ domains: Set<String>,
+ enabled: Boolean,
+ @UserIdInt userId: Int
+) = setDomainVerificationUserSelection(domainSetId, domains.toMutableSet(), enabled, userId)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 0d8f275be09c..377bae15e2d5 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -20,28 +20,27 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageUserState
-import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
-import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.spyThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-import org.mockito.Mockito
+import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
-import org.mockito.Mockito.any
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
@@ -129,13 +128,13 @@ class DomainVerificationSettingsMutationTest {
setDomainVerificationStatus(
TEST_UUID,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service("setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
TEST_PKG,
- DomainVerificationManager.STATE_SUCCESS,
+ DomainVerificationState.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
@@ -144,7 +143,7 @@ class DomainVerificationSettingsMutationTest {
TEST_UID,
TEST_UUID,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service("setLinkHandlingAllowedUserId") {
@@ -266,5 +265,7 @@ class DomainVerificationSettingsMutationTest {
// This doesn't check for visibility; that's done in the enforcer test
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(10)) { true }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 0576125748fb..44c1b8f3fbb9 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
import android.content.pm.verify.domain.DomainVerificationUserState
import android.os.Build
import android.os.PatternMatcher
@@ -74,6 +75,8 @@ class DomainVerificationUserStateOverrideTest {
}).apply {
setConnection(mockThrowOnUnmocked {
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
whenever(scheduleWriteSettings())
// Need to provide an internal UID so some permission checks are ignored
@@ -154,19 +157,20 @@ class DomainVerificationUserStateOverrideTest {
.containsExactly(PKG_TWO)
}
- @Test(expected = IllegalArgumentException::class)
+ @Test
fun anotherPackageTakeoverFailure() {
val service = makeService()
// Verify 1 to give it a higher approval level
service.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
- DomainVerificationManager.STATE_SUCCESS)
+ DomainVerificationState.STATE_SUCCESS)
assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
.containsExactly(PKG_ONE)
// Attempt override by package 2
- service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
+ assertThat(service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true,
+ USER_ID)).isEqualTo(DomainVerificationManager.ERROR_UNABLE_TO_APPROVE)
}
private fun DomainVerificationService.stateFor(pkgName: String, host: String) =
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index f2e85a700327..28940b34c82a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -27,6 +27,7 @@ import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.WINDOW_EXACT;
import static android.app.AlarmManager.WINDOW_HEURISTIC;
+import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -47,17 +48,20 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED;
+import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_CRASH_NON_CLOCK_APPS;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW;
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
@@ -71,6 +75,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,6 +86,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import android.Manifest;
import android.app.ActivityManager;
@@ -408,7 +414,7 @@ public class AlarmManagerServiceTest {
ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass(
IAppOpsCallback.class);
try {
- verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM),
+ verify(mIAppOpsService).startWatchingMode(eq(OP_SCHEDULE_EXACT_ALARM),
isNull(), appOpsCallbackCaptor.capture());
} catch (RemoteException e) {
// Not expected on a mock.
@@ -445,12 +451,12 @@ public class AlarmManagerServiceTest {
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
int flags, int callingUid) {
- setTestAlarm(type, triggerTime, operation, interval, flags, callingUid, null);
+ setTestAlarm(type, triggerTime, 0, operation, interval, flags, callingUid, null);
}
- private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
- int flags, int callingUid, Bundle idleOptions) {
- mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags,
+ private void setTestAlarm(int type, long triggerTime, long windowLength,
+ PendingIntent operation, long interval, int flags, int callingUid, Bundle idleOptions) {
+ mService.setImpl(type, triggerTime, windowLength, interval, operation, null, "test", flags,
null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions);
}
@@ -572,6 +578,7 @@ public class AlarmManagerServiceTest {
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW, 35);
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40);
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45);
+ setDeviceConfigLong(KEY_MIN_WINDOW, 50);
assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL);
@@ -581,6 +588,7 @@ public class AlarmManagerServiceTest {
assertEquals(35, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_WINDOW);
assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(45, mService.mConstants.LISTENER_TIMEOUT);
+ assertEquals(50, mService.mConstants.MIN_WINDOW);
}
@Test
@@ -1644,6 +1652,10 @@ public class AlarmManagerServiceTest {
getNewMockPendingIntent(), null, null, null,
mock(AlarmManager.AlarmClockInfo.class));
+ // exact
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, getNewMockPendingIntent(), null, null, null, null);
+
// exact, allow-while-idle
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
@@ -1658,6 +1670,22 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void exactBinderCallWhenChangeDisabled() throws Exception {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
+ }
+
+ @Test
public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
doReturn(false).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
@@ -1746,7 +1774,34 @@ public class AlarmManagerServiceTest {
}
@Test
- public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
+ public void alarmClockBinderCallWithoutPermission() throws RemoteException {
+ setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ try {
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+ fail("alarm clock binder call succeeded without permission");
+ } catch (SecurityException se) {
+ // Expected.
+ }
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+ }
+
+ @Test
+ public void exactBinderCallWithPermission() throws RemoteException {
doReturn(true).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
@@ -1754,7 +1809,7 @@ public class AlarmManagerServiceTest {
// Permission check is granted by default by the mock.
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
- FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+ 0, alarmPi, null, null, null, null);
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
Manifest.permission.SCHEDULE_EXACT_ALARM));
@@ -1763,7 +1818,7 @@ public class AlarmManagerServiceTest {
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
eq(alarmPi), isNull(), isNull(),
- eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
@@ -1772,7 +1827,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
+ public void exactBinderCallWithAllowlist() throws RemoteException {
doReturn(true).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
@@ -1784,7 +1839,7 @@ public class AlarmManagerServiceTest {
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
- FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+ 0, alarmPi, null, null, null, null);
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
Manifest.permission.SCHEDULE_EXACT_ALARM));
@@ -1793,7 +1848,30 @@ public class AlarmManagerServiceTest {
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
eq(alarmPi), isNull(), isNull(),
- eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
+ eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+ System.out.println("what got captured: " + bundleCaptor.getValue());
+ }
+
+ @Test
+ public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // Permission check is granted by default by the mock.
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(),
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
@@ -1802,44 +1880,83 @@ public class AlarmManagerServiceTest {
}
@Test
- public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
+ public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
doReturn(true).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
-
+ // If permission is denied, only then allowlist will be checked.
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+
final PendingIntent alarmPi = getNewMockPendingIntent();
- mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
- Manifest.permission.SCHEDULE_EXACT_ALARM), never());
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
- eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
- isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
final int type = idleOptions.getTemporaryAppAllowlistType();
- assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ // App is on power allowlist, doesn't need explicit FGS grant in broadcast options.
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type);
}
@Test
- public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException {
+ public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException {
+ setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true);
doReturn(true).when(
() -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
anyString(), any(UserHandle.class)));
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ try {
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, alarmPi, null, null, null, null);
+ fail("exact binder call succeeded without permission");
+ } catch (SecurityException se) {
+ // Expected.
+ }
+ try {
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+ fail("exact, allow-while-idle binder call succeeded without permission");
+ } catch (SecurityException se) {
+ // Expected.
+ }
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM), times(2));
+ verify(mDeviceIdleInternal, times(2)).isAppOnWhitelist(anyInt());
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCall() throws RemoteException {
+ // Both permission and power exemption status don't matter for these alarms.
+ // We only want to test that the flags and idleOptions are correct.
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
Manifest.permission.SCHEDULE_EXACT_ALARM), never());
- verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
@@ -1852,13 +1969,104 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void binderCallWithUserAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+ when(mAppStateTracker.isUidPowerSaveUserExempt(Process.myUid())).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
+ }
+
+ @Test
+ public void minWindow() {
+ final long minWindow = 73;
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(minWindow, a.windowLength);
+ }
+ }
+
+ @Test
+ public void opScheduleExactAlarmRevoked() throws Exception {
+ when(mIAppOpsService.checkOperation(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(AppOpsManager.MODE_ERRORED);
+ mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
+ verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE);
+ }
+
+ @Test
+ public void removeExactAlarmsOnPermissionRevoked() {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // basic exact alarm
+ setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID,
+ null);
+ // exact and allow-while-idle alarm
+ setTestAlarm(ELAPSED_REALTIME, 0, 0, getNewMockPendingIntent(), 0, FLAG_ALLOW_WHILE_IDLE,
+ TEST_CALLING_UID, null);
+ // alarm clock
+ setWakeFromIdle(RTC_WAKEUP, 0, getNewMockPendingIntent());
+
+ final PendingIntent inexact = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, 10, inexact, 0, 0, TEST_CALLING_UID, null);
+
+ final PendingIntent inexactAwi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, 10, inexactAwi, 0, FLAG_ALLOW_WHILE_IDLE,
+ TEST_CALLING_UID, null);
+
+ final PendingIntent exactButDifferentUid = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, 0, exactButDifferentUid, 0, 0, TEST_CALLING_UID + 5,
+ null);
+ assertEquals(6, mService.mAlarmStore.size());
+
+ mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+
+ final ArrayList<Alarm> remaining = mService.mAlarmStore.asList();
+ assertEquals(3, remaining.size());
+ assertTrue("Basic inexact alarm removed",
+ remaining.removeIf(a -> a.matches(inexact, null)));
+ assertTrue("Inexact allow-while-idle alarm removed",
+ remaining.removeIf(a -> a.matches(inexactAwi, null)));
+ assertTrue("Alarm from different uid removed",
+ remaining.removeIf(a -> a.matches(exactButDifferentUid, null)));
+
+ // Mock should return false by default.
+ verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID));
+ }
+
+ @Test
public void idleOptionsSentOnExpiration() throws Exception {
final long triggerTime = mNowElapsedTest + 5000;
final PendingIntent alarmPi = getNewMockPendingIntent();
final Bundle idleOptions = new Bundle();
idleOptions.putChar("TEST_CHAR_KEY", 'x');
idleOptions.putInt("TEST_INT_KEY", 53);
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi, 0, 0, TEST_CALLING_UID,
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, 0, alarmPi, 0, 0, TEST_CALLING_UID,
idleOptions);
mNowElapsedTest = mTestTimer.getElapsed();
@@ -1885,6 +2093,7 @@ public class AlarmManagerServiceTest {
assertTrue(i + "th PendingIntent missing: ",
alarmsBefore.removeIf(a -> a.matches(pi, null)));
}
+ assertEquals(BatchingAlarmStore.TAG, mService.mAlarmStore.getName());
setDeviceConfigBoolean(KEY_LAZY_BATCHING, true);
@@ -1895,6 +2104,7 @@ public class AlarmManagerServiceTest {
assertTrue(i + "th PendingIntent missing: ",
alarmsAfter.removeIf(a -> a.matches(pi, null)));
}
+ assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName());
}
@After
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 29db7409131e..7e4bc1e371b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1880,6 +1880,144 @@ public class QuotaControllerTest {
}
@Test
+ public void testIsWithinEJQuotaLocked_NeverApp() {
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
+ setStandbyBucket(NEVER_INDEX, js);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_Charging() {
+ setCharging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_Charging", 1);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_UnderDuration() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_UnderDuration", 1);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_OverDuration() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_OverDuration", 1);
+ setStandbyBucket(FREQUENT_INDEX, js);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
+ public void testIsWithinEJQuotaLocked_TimingSession() {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 8 * MINUTE_IN_MILLIS);
+
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TimingSession", 1);
+ for (int i = 0; i < 25; ++i) {
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
+ 2), true);
+
+ synchronized (mQuotaController.mLock) {
+ setStandbyBucket(ACTIVE_INDEX, js);
+ assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
+ i < 19, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(WORKING_INDEX, js);
+ assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
+ i < 14, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(FREQUENT_INDEX, js);
+ assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
+ i < 12, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(RARE_INDEX, js);
+ assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
+ i < 9, mQuotaController.isWithinEJQuotaLocked(js));
+
+ setStandbyBucket(RESTRICTED_INDEX, js);
+ assertEquals("Restricted has incorrect quota status with " + (i + 1) + " sessions",
+ i < 7, mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+ }
+
+ /**
+ * Tests that Timers properly track sessions when an app is added and removed from the temp
+ * allowlist.
+ */
+ @Test
+ public void testIsWithinEJQuotaLocked_TempAllowlisting() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting", 1);
+ setStandbyBucket(FREQUENT_INDEX, js);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
+ setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
+ Handler handler = mQuotaController.getHandler();
+ spyOn(handler);
+
+ // Apps on the temp allowlist should be able to schedule & start EJs, even if they're out
+ // of quota (as long as they are in the temp allowlist grace period).
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ // Still in grace period
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ advanceElapsedClock(6 * SECOND_IN_MILLIS);
+ // Out of grace period.
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
+ @Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
synchronized (mQuotaController.mLock) {
@@ -3881,6 +4019,55 @@ public class QuotaControllerTest {
}
@Test
+ public void testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
+
+ for (int i = 1; i <= 25; ++i) {
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
+ 2), true);
+
+ synchronized (mQuotaController.mLock) {
+ setStandbyBucket(ACTIVE_INDEX);
+ assertEquals("Active has incorrect remaining EJ time with " + i + " sessions",
+ (20 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(WORKING_INDEX);
+ assertEquals("Working has incorrect remaining EJ time with " + i + " sessions",
+ (15 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(FREQUENT_INDEX);
+ assertEquals("Frequent has incorrect remaining EJ time with " + i + " sessions",
+ (13 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RARE_INDEX);
+ assertEquals("Rare has incorrect remaining EJ time with " + i + " sessions",
+ (10 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RESTRICTED_INDEX);
+ assertEquals("Restricted has incorrect remaining EJ time with " + i + " sessions",
+ (5 - i) * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingEJExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+ }
+
+ @Test
public void testGetTimeUntilEJQuotaConsumedLocked_NoHistory() {
final long[] limits = mQuotaController.getEJLimitsMs();
for (int i = 0; i < limits.length; ++i) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index ee0a16a70265..2e0cadf264cf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.server.pm.dex;
@@ -28,28 +28,34 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.BatteryManager;
import android.os.Build;
+import android.os.PowerManager;
import android.os.UserHandle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.server.pm.Installer;
+import com.android.server.pm.PackageManagerService;
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
import java.io.File;
@@ -69,9 +75,15 @@ public class DexManagerTests {
DelegateLastClassLoader.class.getName();
private static final String UNSUPPORTED_CLASS_LOADER_NAME = "unsupported.class_loader";
- @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ private static final int TEST_BATTERY_LEVEL_CRITICAL = 10;
+ private static final int TEST_BATTERY_LEVEL_DEFAULT = 80;
+
+ public StaticMockitoSession mMockitoSession;
@Mock Installer mInstaller;
@Mock IPackageManager mPM;
+ @Mock BatteryManager mMockBatteryManager;
+ @Mock PowerManager mMockPowerManager;
+
private final Object mInstallLock = new Object();
private DexManager mDexManager;
@@ -117,7 +129,37 @@ public class DexManagerTests {
mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
- mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
+ // Initialize Static Mocking
+
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Mock....
+
+ mMockBatteryManager = ExtendedMockito.mock(BatteryManager.class);
+ mMockPowerManager = ExtendedMockito.mock(PowerManager.class);
+
+ setDefaultMockValues();
+
+ Resources mockResources = ExtendedMockito.mock(Resources.class);
+ ExtendedMockito.when(mockResources
+ .getInteger(com.android.internal.R.integer.config_criticalBatteryWarningLevel))
+ .thenReturn(15);
+
+ Context mockContext = ExtendedMockito.mock(Context.class);
+ ExtendedMockito.doReturn(mockResources)
+ .when(mockContext)
+ .getResources();
+ ExtendedMockito.doReturn(mMockBatteryManager)
+ .when(mockContext)
+ .getSystemService(BatteryManager.class);
+ ExtendedMockito.doReturn(mMockPowerManager)
+ .when(mockContext)
+ .getSystemService(PowerManager.class);
+
+ mDexManager = new DexManager(mockContext, mPM, /*PackageDexOptimizer*/ null,
mInstaller, mInstallLock);
// Foo and Bar are available to user0.
@@ -128,6 +170,25 @@ public class DexManagerTests {
mDexManager.load(existingPackages);
}
+ @After
+ public void teardown() throws Exception {
+ mMockitoSession.finishMocking();
+ }
+
+ private void setDefaultMockValues() {
+ ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_DISCHARGING)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+ ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_DEFAULT)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+ ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_NONE)
+ .when(mMockPowerManager)
+ .getCurrentThermalStatus();
+ }
+
@Test
public void testNotifyPrimaryUse() {
// The main dex file and splits are re-loaded by the app.
@@ -633,6 +694,114 @@ public class DexManagerTests {
assertNoDclInfo(mSystemServerJarInvalid);
}
+ @Test
+ public void testInstallScenarioToReasonDefault() {
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
+ @Test
+ public void testInstallScenarioToReasonThermal() {
+ ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_SEVERE)
+ .when(mMockPowerManager)
+ .getCurrentThermalStatus();
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
+ @Test
+ public void testInstallScenarioToReasonBatteryDischarging() {
+ ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
+ @Test
+ public void testInstallScenarioToReasonBatteryCharging() {
+ ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+ ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_CHARGING)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
new file mode 100644
index 000000000000..5a4431ee8c89
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
@@ -0,0 +1,2 @@
+calin@google.com
+ngeoffray@google.com
diff --git a/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
index ef20ee7e6ecd..8ecb07158564 100644
--- a/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
@@ -16,59 +16,74 @@
package com.android.server.power;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import static com.android.server.power.FaceDownDetector.KEY_FEATURE_ENABLED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
+import android.os.PowerManager;
+import android.provider.DeviceConfig;
import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.display.TestUtils;
+import com.android.server.testables.TestableDeviceConfig;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class FaceDownDetectorTest {
@ClassRule
public static final TestableContext sContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
- private final FaceDownDetector mFaceDownDetector =
- new FaceDownDetector(this::onFlip);
+ private final FaceDownDetector mFaceDownDetector = new FaceDownDetector(this::onFlip);
@Mock private SensorManager mSensorManager;
+ @Mock private PowerManager mPowerManager;
- private long mCurrentTime;
- private int mOnFaceDownCalls = 0;
- private int mOnFaceDownExitCalls = 0;
+ private Duration mCurrentTime;
+ private int mOnFaceDownCalls;
+ private int mOnFaceDownExitCalls;
@Before
- public void setup() {
+ public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
sContext.addMockSystemService(SensorManager.class, mSensorManager);
- mCurrentTime = 0;
+ sContext.addMockSystemService(PowerManager.class, mPowerManager);
+ doReturn(true).when(mPowerManager).isInteractive();
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_FEATURE_ENABLED, "true", false);
+ mCurrentTime = Duration.ZERO;
+ mOnFaceDownCalls = 0;
+ mOnFaceDownExitCalls = 0;
}
@Test
public void faceDownFor2Seconds_triggersFaceDown() throws Exception {
mFaceDownDetector.systemReady(sContext);
- // Face up
- // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
-
- for (int i = 0; i < 200; i++) {
- advanceTime(Duration.ofMillis(20));
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
- }
+ triggerFaceDown();
assertThat(mOnFaceDownCalls).isEqualTo(1);
assertThat(mOnFaceDownExitCalls).isEqualTo(0);
@@ -111,15 +126,7 @@ public class FaceDownDetectorTest {
public void faceDownFor2Seconds_followedByFaceUp_triggersFaceDownExit() throws Exception {
mFaceDownDetector.systemReady(sContext);
- // Face up
- // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
-
- // Trigger face down
- for (int i = 0; i < 100; i++) {
- advanceTime(Duration.ofMillis(20));
- mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
- }
+ triggerFaceDown();
// Phone flips
for (int i = 0; i < 10; i++) {
@@ -131,8 +138,71 @@ public class FaceDownDetectorTest {
assertThat(mOnFaceDownExitCalls).isEqualTo(1);
}
+ @Test
+ public void notInteractive_doesNotTriggerFaceDown() throws Exception {
+ doReturn(false).when(mPowerManager).isInteractive();
+ mFaceDownDetector.systemReady(sContext);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ assertThat(mOnFaceDownExitCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void afterDisablingFeature_doesNotTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+ setEnabled(false);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void afterReenablingWhileNonInteractive_doesNotTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+ setEnabled(false);
+
+ doReturn(false).when(mPowerManager).isInteractive();
+ setEnabled(true);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void afterReenablingWhileInteractive_doesTriggerFaceDown() throws Exception {
+ mFaceDownDetector.systemReady(sContext);
+ setEnabled(false);
+
+ setEnabled(true);
+
+ triggerFaceDown();
+
+ assertThat(mOnFaceDownCalls).isEqualTo(1);
+ }
+
+ private void triggerFaceDown() throws Exception {
+ // Face up
+ // Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
+
+ for (int i = 0; i < 200; i++) {
+ advanceTime(Duration.ofMillis(20));
+ mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
+ }
+ }
+
+ private void setEnabled(Boolean enabled) throws Exception {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_FEATURE_ENABLED, enabled.toString(), false);
+ waitForListenerToHandle();
+ }
+
private void advanceTime(Duration duration) {
- mCurrentTime += duration.toNanos();
+ mCurrentTime = mCurrentTime.plus(duration);
}
/**
@@ -146,12 +216,11 @@ public class FaceDownDetectorTest {
SensorEvent.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
final SensorEvent event = constructor.newInstance(3);
- event.sensor =
- TestUtils.createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
+ event.sensor = createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
event.values[0] = x;
event.values[1] = y;
event.values[2] = gravity;
- event.timestamp = mCurrentTime;
+ event.timestamp = mCurrentTime.toNanos();
return event;
}
@@ -162,4 +231,25 @@ public class FaceDownDetectorTest {
mOnFaceDownExitCalls++;
}
}
+
+ private Sensor createSensor(int type, String strType) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ if (strType != null) {
+ Field f = sensor.getClass().getDeclaredField("mStringType");
+ f.setAccessible(true);
+ f.set(sensor, strType);
+ }
+ return sensor;
+ }
+
+ private void waitForListenerToHandle() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ sContext.getMainExecutor().execute(latch::countDown);
+ assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+ }
}
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/OwnersTest/OWNERS b/services/tests/servicestests/assets/OwnersTest/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index c7e7c7861370..45f43e8b672f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
@@ -30,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -47,10 +50,13 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.testing.DexmakerShareClassLoaderRule;
+import android.testing.TestableContext;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -58,7 +64,9 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
/**
@@ -72,9 +80,9 @@ public class AccessibilitySecurityPolicyTest {
private static final int APP_UID = 10400;
private static final int APP_PID = 2000;
private static final int SYSTEM_PID = 558;
-
- private static final String PERMISSION = "test-permission";
- private static final String FUNCTION = "test-function-name";
+ private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+ private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+ "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
private static final int[] ALWAYS_DISPATCH_EVENTS = {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -108,29 +116,51 @@ public class AccessibilitySecurityPolicyTest {
private AccessibilitySecurityPolicy mA11ySecurityPolicy;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ getInstrumentation().getTargetContext(), null);
+
// To mock package-private class
- @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
- @Mock private Context mMockContext;
- @Mock private PackageManager mMockPackageManager;
- @Mock private UserManager mMockUserManager;
- @Mock private AppOpsManager mMockAppOpsManager;
- @Mock private AccessibilityServiceConnection mMockA11yServiceConnection;
- @Mock private AccessibilityWindowManager mMockA11yWindowManager;
- @Mock private AppWidgetManagerInternal mMockAppWidgetManager;
- @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock
+ private PackageManager mMockPackageManager;
+ @Mock
+ private UserManager mMockUserManager;
+ @Mock
+ private AppOpsManager mMockAppOpsManager;
+ @Mock
+ private AccessibilityServiceConnection mMockA11yServiceConnection;
+ @Mock
+ private AccessibilityWindowManager mMockA11yWindowManager;
+ @Mock
+ private AppWidgetManagerInternal mMockAppWidgetManager;
+ @Mock
+ private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock
+ private AccessibilityServiceInfo mMockA11yServiceInfo;
+ @Mock
+ private PolicyWarningUIController mPolicyWarningUIController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
- when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
- when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
+ mContext.setMockPackageManager(mMockPackageManager);
+ mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
+ mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
+ mContext.getOrCreateTestableResources().addOverride(
+ R.dimen.accessibility_focus_highlight_stroke_width, 1);
- mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager);
+ when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
+ when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+
+ mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
+ mPolicyWarningUIController, mContext, mMockA11yUserManager);
mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
+ mA11ySecurityPolicy.onSwitchUserLocked(TEST_USER_ID, new HashSet<>());
when(mMockA11yWindowManager.resolveParentWindowIdLocked(anyInt())).then(returnsFirstArg());
}
@@ -141,7 +171,7 @@ public class AccessibilitySecurityPolicyTest {
final AccessibilityEvent event = AccessibilityEvent.obtain(ALWAYS_DISPATCH_EVENTS[i]);
assertTrue("Should dispatch [" + event + "]",
mA11ySecurityPolicy.canDispatchAccessibilityEventLocked(
- UserHandle.USER_SYSTEM,
+ TEST_USER_ID,
event));
}
}
@@ -154,28 +184,28 @@ public class AccessibilitySecurityPolicyTest {
event.setWindowId(invalidWindowId);
assertFalse("Shouldn't dispatch [" + event + "]",
mA11ySecurityPolicy.canDispatchAccessibilityEventLocked(
- UserHandle.USER_SYSTEM,
+ TEST_USER_ID,
event));
}
}
@Test
public void canDispatchAccessibilityEvent_otherEvents_windowIdIsActive_returnTrue() {
- when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM))
+ when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID))
.thenReturn(WINDOWID);
for (int i = 0; i < OTHER_EVENTS.length; i++) {
final AccessibilityEvent event = AccessibilityEvent.obtain(OTHER_EVENTS[i]);
event.setWindowId(WINDOWID);
assertTrue("Should dispatch [" + event + "]",
mA11ySecurityPolicy.canDispatchAccessibilityEventLocked(
- UserHandle.USER_SYSTEM,
+ TEST_USER_ID,
event));
}
}
@Test
public void canDispatchAccessibilityEvent_otherEvents_windowIdExist_returnTrue() {
- when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM))
+ when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID))
.thenReturn(WINDOWID2);
when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
.thenReturn(AccessibilityWindowInfo.obtain());
@@ -184,7 +214,7 @@ public class AccessibilitySecurityPolicyTest {
event.setWindowId(WINDOWID);
assertTrue("Should dispatch [" + event + "]",
mA11ySecurityPolicy.canDispatchAccessibilityEventLocked(
- UserHandle.USER_SYSTEM,
+ TEST_USER_ID,
event));
}
}
@@ -192,24 +222,24 @@ public class AccessibilitySecurityPolicyTest {
@Test
public void resolveValidReportedPackage_nullPkgName_returnNull() {
assertNull(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- null, Process.SYSTEM_UID, UserHandle.USER_SYSTEM, SYSTEM_PID));
+ null, Process.SYSTEM_UID, TEST_USER_ID, SYSTEM_PID));
}
@Test
public void resolveValidReportedPackage_uidIsSystem_returnPkgName() {
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- PACKAGE_NAME, Process.SYSTEM_UID, UserHandle.USER_SYSTEM, SYSTEM_PID),
+ PACKAGE_NAME, Process.SYSTEM_UID, TEST_USER_ID, SYSTEM_PID),
PACKAGE_NAME);
}
@Test
public void resolveValidReportedPackage_uidAndPkgNameMatched_returnPkgName()
throws PackageManager.NameNotFoundException {
- when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME, UserHandle.USER_SYSTEM))
+ when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME, TEST_USER_ID))
.thenReturn(APP_UID);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- PACKAGE_NAME, APP_UID, UserHandle.USER_SYSTEM, APP_PID),
+ PACKAGE_NAME, APP_UID, TEST_USER_ID, APP_PID),
PACKAGE_NAME);
}
@@ -225,11 +255,11 @@ public class AccessibilitySecurityPolicyTest {
when(mMockAppWidgetManager.getHostedWidgetPackages(widgetHostUid))
.thenReturn(widgetPackages);
- when(mMockPackageManager.getPackageUidAsUser(hostPackageName, UserHandle.USER_SYSTEM))
+ when(mMockPackageManager.getPackageUidAsUser(hostPackageName, TEST_USER_ID))
.thenReturn(widgetHostUid);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- widgetPackageName, widgetHostUid, UserHandle.USER_SYSTEM, widgetHostPid),
+ widgetPackageName, widgetHostUid, TEST_USER_ID, widgetHostPid),
widgetPackageName);
}
@@ -240,16 +270,16 @@ public class AccessibilitySecurityPolicyTest {
final String[] uidPackages = {PACKAGE_NAME, PACKAGE_NAME2};
when(mMockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(uidPackages);
- when(mMockPackageManager.getPackageUidAsUser(invalidPackageName, UserHandle.USER_SYSTEM))
+ when(mMockPackageManager.getPackageUidAsUser(invalidPackageName, TEST_USER_ID))
.thenThrow(PackageManager.NameNotFoundException.class);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
- when(mMockContext.checkPermission(
- eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
+ mContext.getTestablePermissions().setPermission(
+ Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY,
+ PackageManager.PERMISSION_DENIED);
assertEquals(PACKAGE_NAME, mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- invalidPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID));
+ invalidPackageName, APP_UID, TEST_USER_ID, APP_PID));
}
@Test
@@ -260,16 +290,16 @@ public class AccessibilitySecurityPolicyTest {
final String[] uidPackages = {PACKAGE_NAME};
when(mMockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(uidPackages);
- when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, UserHandle.USER_SYSTEM))
+ when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, TEST_USER_ID))
.thenReturn(wantedUid);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
- when(mMockContext.checkPermission(
- eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID)))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ mContext.getTestablePermissions().setPermission(
+ Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY,
+ PackageManager.PERMISSION_GRANTED);
assertEquals(wantedPackageName, mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- wantedPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID));
+ wantedPackageName, APP_UID, TEST_USER_ID, APP_PID));
}
@Test
@@ -280,16 +310,16 @@ public class AccessibilitySecurityPolicyTest {
final String[] uidPackages = {PACKAGE_NAME};
when(mMockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(uidPackages);
- when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, UserHandle.USER_SYSTEM))
+ when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, TEST_USER_ID))
.thenReturn(wantedUid);
when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID))
.thenReturn(new ArraySet<>());
- when(mMockContext.checkPermission(
- eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
+ mContext.getTestablePermissions().setPermission(
+ Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY,
+ PackageManager.PERMISSION_DENIED);
assertEquals(PACKAGE_NAME, mA11ySecurityPolicy.resolveValidReportedPackageLocked(
- wantedPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID));
+ wantedPackageName, APP_UID, TEST_USER_ID, APP_PID));
}
@Test
@@ -301,7 +331,7 @@ public class AccessibilitySecurityPolicyTest {
@Test
public void computeValidReportedPackages_uidIsAppWidgetHost_returnTargetAndWidgetName() {
final int widgetHostUid = APP_UID;
- final String targetPackageName = PACKAGE_NAME;
+ final String targetPackageName = PACKAGE_NAME;
final String widgetPackageName = PACKAGE_NAME2;
final ArraySet<String> widgetPackages = new ArraySet<>();
widgetPackages.add(widgetPackageName);
@@ -320,7 +350,7 @@ public class AccessibilitySecurityPolicyTest {
when(mMockA11yServiceConnection.getCapabilities())
.thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
- assertFalse(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM,
+ assertFalse(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(TEST_USER_ID,
mMockA11yServiceConnection, invalidWindowId));
}
@@ -328,10 +358,10 @@ public class AccessibilitySecurityPolicyTest {
public void canGetAccessibilityNodeInfo_hasCapAndWindowIsActive_returnTrue() {
when(mMockA11yServiceConnection.getCapabilities())
.thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
- when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM))
+ when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID))
.thenReturn(WINDOWID);
- assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM,
+ assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(TEST_USER_ID,
mMockA11yServiceConnection, WINDOWID));
}
@@ -339,12 +369,12 @@ public class AccessibilitySecurityPolicyTest {
public void canGetAccessibilityNodeInfo_hasCapAndWindowExist_returnTrue() {
when(mMockA11yServiceConnection.getCapabilities())
.thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
- when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM))
+ when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID))
.thenReturn(WINDOWID2);
when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
.thenReturn(AccessibilityWindowInfo.obtain());
- assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM,
+ assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(TEST_USER_ID,
mMockA11yServiceConnection, WINDOWID));
}
@@ -464,8 +494,10 @@ public class AccessibilitySecurityPolicyTest {
.thenReturn(currentUserId);
doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
callingUserId);
- when(mMockContext.checkCallingPermission(any()))
- .thenReturn(PackageManager.PERMISSION_DENIED);
+ mContext.getTestablePermissions().setPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+ PackageManager.PERMISSION_DENIED);
+ mContext.getTestablePermissions().setPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, PackageManager.PERMISSION_DENIED);
spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.USER_CURRENT_OR_SELF);
@@ -482,8 +514,8 @@ public class AccessibilitySecurityPolicyTest {
.thenReturn(currentUserId);
doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
callingUserId);
- when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ mContext.getTestablePermissions().setPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+ PackageManager.PERMISSION_GRANTED);
assertEquals(wantedUserId,
spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId));
@@ -500,8 +532,8 @@ public class AccessibilitySecurityPolicyTest {
.thenReturn(currentUserId);
doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
callingUserId);
- when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ mContext.getTestablePermissions().setPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, PackageManager.PERMISSION_GRANTED);
assertEquals(wantedUserId,
spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId));
@@ -518,10 +550,10 @@ public class AccessibilitySecurityPolicyTest {
.thenReturn(currentUserId);
doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked(
callingUserId);
- when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL))
- .thenReturn(PackageManager.PERMISSION_DENIED);
+ mContext.getTestablePermissions().setPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+ PackageManager.PERMISSION_DENIED);
+ mContext.getTestablePermissions().setPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, PackageManager.PERMISSION_DENIED);
spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId);
}
@@ -562,4 +594,57 @@ public class AccessibilitySecurityPolicyTest {
APP_UID, PACKAGE_NAME);
}
+ @Test
+ public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() {
+ onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction();
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+ any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
+ }
+
+ @Test
+ public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() {
+ onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
+ }
+
+ @Test
+ public void onSwitchUser_differentUser_activateUIControllerAction() {
+ onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+
+ mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());
+
+ verify(mPolicyWarningUIController).onSwitchUserLocked(eq(2), eq(new HashSet<>()));
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
new file mode 100644
index 000000000000..01a641f141ad
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static android.app.AlarmManager.RTC_WAKEUP;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_VIEW_AND_CONTROL_ACCESS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.StatusBarManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests for the {@link PolicyWarningUIController}.
+ */
+public class PolicyWarningUIControllerTest {
+ private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+ private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+ "com.android.server.accessibility", "PolicyWarningUIControllerTest");
+
+ private final List<AccessibilityServiceInfo> mEnabledServiceList = new ArrayList<>();
+
+ @Rule
+ public final A11yTestableContext mContext = new A11yTestableContext(
+ getInstrumentation().getTargetContext());
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private NotificationManager mNotificationManager;
+ @Mock
+ private StatusBarManager mStatusBarManager;
+ @Mock
+ private AccessibilityServiceInfo mMockA11yServiceInfo;
+ @Mock
+ private ResolveInfo mMockResolveInfo;
+ @Mock
+ private ServiceInfo mMockServiceInfo;
+ @Mock
+ private Context mSpyContext;
+ @Mock
+ private AccessibilitySecurityPolicy mAccessibilitySecurityPolicy;
+
+ private PolicyWarningUIController mPolicyWarningUIController;
+ private FakeNotificationController mFakeNotificationController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(AlarmManager.class, mAlarmManager);
+ mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
+ mContext.addMockSystemService(StatusBarManager.class, mStatusBarManager);
+ mFakeNotificationController = new FakeNotificationController(mContext);
+ mPolicyWarningUIController = new PolicyWarningUIController(
+ getInstrumentation().getTargetContext().getMainThreadHandler(), mContext,
+ mFakeNotificationController);
+ mPolicyWarningUIController.setAccessibilityPolicyManager(mAccessibilitySecurityPolicy);
+ mPolicyWarningUIController.onSwitchUserLocked(TEST_USER_ID, new HashSet<>());
+ mEnabledServiceList.clear();
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
+ "", TEST_USER_ID);
+ }
+
+ @Test
+ public void receiveActionSendNotification_isNonA11yCategoryService_sendNotification() {
+ mEnabledServiceList.add(mMockA11yServiceInfo);
+ mMockResolveInfo.serviceInfo = mMockServiceInfo;
+ when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
+ when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
+ when(mAccessibilitySecurityPolicy.isA11yCategoryService(
+ mMockA11yServiceInfo)).thenReturn(false);
+
+ mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID,
+ PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
+ TEST_COMPONENT_NAME.flattenToShortString()));
+
+ verify(mNotificationManager).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()),
+ eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(
+ Notification.class));
+ }
+
+ @Test
+ public void receiveActionA11ySettings_launchA11ySettingsAndDismissNotification() {
+ mFakeNotificationController.onReceive(mContext,
+ createIntent(TEST_USER_ID, PolicyWarningUIController.ACTION_A11Y_SETTINGS,
+ TEST_COMPONENT_NAME.flattenToShortString()));
+
+ verifyLaunchA11ySettings();
+ verify(mNotificationManager).cancel(TEST_COMPONENT_NAME.flattenToShortString(),
+ NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
+ assertNotifiedSettingsEqual(TEST_USER_ID,
+ TEST_COMPONENT_NAME.flattenToShortString());
+ }
+
+ @Test
+ public void receiveActionDismissNotification_addToNotifiedSettings() {
+ mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID,
+ PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION,
+ TEST_COMPONENT_NAME.flattenToShortString()));
+
+ assertNotifiedSettingsEqual(TEST_USER_ID,
+ TEST_COMPONENT_NAME.flattenToShortString());
+ }
+
+ @Test
+ public void onEnabledServicesChangedLocked_serviceDisabled_removedFromNotifiedSettings() {
+ final Set<ComponentName> enabledServices = new HashSet<>();
+ enabledServices.add(TEST_COMPONENT_NAME);
+ mPolicyWarningUIController.onEnabledServicesChangedLocked(TEST_USER_ID, enabledServices);
+ getInstrumentation().waitForIdleSync();
+ receiveActionDismissNotification_addToNotifiedSettings();
+
+ mPolicyWarningUIController.onEnabledServicesChangedLocked(TEST_USER_ID, new HashSet<>());
+ getInstrumentation().waitForIdleSync();
+
+ assertNotifiedSettingsEqual(TEST_USER_ID, "");
+ }
+
+ @Test
+ public void onNonA11yCategoryServiceBound_setAlarm() {
+ mPolicyWarningUIController.onNonA11yCategoryServiceBound(TEST_USER_ID, TEST_COMPONENT_NAME);
+ getInstrumentation().waitForIdleSync();
+
+ verify(mAlarmManager).set(eq(RTC_WAKEUP), anyLong(),
+ eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID,
+ PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
+ TEST_COMPONENT_NAME.flattenToShortString())));
+ }
+
+ @Test
+ public void onNonA11yCategoryServiceUnbound_cancelAlarm() {
+ mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(TEST_USER_ID,
+ TEST_COMPONENT_NAME);
+ getInstrumentation().waitForIdleSync();
+
+ verify(mAlarmManager).cancel(
+ eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID,
+ PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
+ TEST_COMPONENT_NAME.flattenToShortString())));
+ }
+
+ private void assertNotifiedSettingsEqual(int userId, String settingString) {
+ final String notifiedServicesSetting = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
+ userId);
+ assertEquals(settingString, notifiedServicesSetting);
+ }
+
+ private Intent createIntent(int userId, String action, String serviceComponentName) {
+ final Intent intent = new Intent(action);
+ intent.setPackage(mContext.getPackageName())
+ .setIdentifier(serviceComponentName)
+ .putExtra(Intent.EXTRA_USER_ID, userId);
+ return intent;
+ }
+
+ private void verifyLaunchA11ySettings() {
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(
+ UserHandle.class);
+ verify(mSpyContext).startActivityAsUser(intentCaptor.capture(),
+ any(), userHandleCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction()).isEqualTo(
+ Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER_ID);
+ verify(mStatusBarManager).collapsePanels();
+ }
+
+ private class A11yTestableContext extends TestableContext {
+ A11yTestableContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ mSpyContext.startActivityAsUser(intent, options, user);
+ }
+ }
+
+ private class FakeNotificationController extends
+ PolicyWarningUIController.NotificationController {
+ FakeNotificationController(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected List<AccessibilityServiceInfo> getEnabledServiceInfos() {
+ return mEnabledServiceList;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 44b9f44fc843..872b95548655 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1114,6 +1114,30 @@ public class FullScreenMagnificationControllerTest {
argThat(closeTo(newEndSpec)));
}
+ @Test
+ public void testSetScale_toMagnifying_shouldNotifyActivatedState() {
+ setScaleToMagnifying();
+
+ verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(true));
+ }
+
+ @Test
+ public void testReset_afterMagnifying_shouldNotifyDeactivatedState() {
+ setScaleToMagnifying();
+
+ mFullScreenMagnificationController.reset(DISPLAY_0, mAnimationCallback);
+ verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false));
+ }
+
+ private void setScaleToMagnifying() {
+ register(DISPLAY_0);
+ float scale = 2.0f;
+ PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+
+ mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y,
+ false, SERVICE_ID_1);
+ }
+
private void initMockWindowManager() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 0c3640c4eeb0..84c76b77018d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
import static org.junit.Assert.assertEquals;
@@ -24,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
@@ -283,6 +287,27 @@ public class MagnificationControllerTest {
}
@Test
+ public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() {
+ mMagnificationController.onWindowMagnificationActivationState(true);
+
+ mMagnificationController.onWindowMagnificationActivationState(false);
+
+ verify(mMagnificationController).logMagnificationUsageState(
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), anyLong());
+ }
+
+ @Test
+ public void
+ onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() {
+ mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+ mMagnificationController.onFullScreenMagnificationActivationState(false);
+
+ verify(mMagnificationController).logMagnificationUsageState(
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), anyLong());
+ }
+
+ @Test
public void onTouchInteractionStart_fullScreenAndCapabilitiesAll_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index f26c86c69587..955217c7d93f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -392,6 +392,23 @@ public class WindowMagnificationManagerTest {
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
+ @Test
+ public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ verify(mMockCallback).onWindowMagnificationActivationState(eq(true));
+ }
+
+ @Test
+ public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, true);
+
+ verify(mMockCallback).onWindowMagnificationActivationState(eq(false));
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 129d2633ae92..e13597dc6982 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -19,23 +19,37 @@ package com.android.server.am;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.ActivityManager;
+import android.app.ActivityManager.OnUidImportanceListener;
import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -65,6 +79,12 @@ public class ActivityManagerTest {
private static final long AWAIT_TIMEOUT = 2000;
private static final long CHECK_INTERVAL = 100;
+ private static final String TEST_FGS_CLASS =
+ "com.android.servicestests.apps.simpleservicetestapp.SimpleFgService";
+ private static final String ACTION_FGS_STATS_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+ private static final String EXTRA_MESSENGER = "extra_messenger";
+
private IActivityManager mService;
private IRemoteCallback mCallback;
private Context mContext;
@@ -204,4 +224,184 @@ public class ActivityManagerTest {
public void onServiceDisconnected(ComponentName name) {
}
}
+
+ /**
+ * Note: This test actually only works in eng build. It'll always pass
+ * in user and userdebug build, because the expected exception won't be
+ * thrown in those builds.
+ */
+ @LargeTest
+ @Test
+ public void testFgsProcStatsTracker() throws Exception {
+ final PackageManager pm = mContext.getPackageManager();
+ final long timeout = 5000;
+ int uid = pm.getPackageUid(TEST_APP, 0);
+ final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
+ final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final H handler = new H(Looper.getMainLooper(), latchHolder);
+ final Messenger messenger = new Messenger(handler);
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ final CountDownLatch dboxLatch = new CountDownLatch(1);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String tag_wtf = "system_server_wtf";
+ if (tag_wtf.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
+ final DropBoxManager.Entry e = dbox.getNextEntry(tag_wtf, intent.getLongExtra(
+ DropBoxManager.EXTRA_TIME, 0) - 1);
+ final String text = e.getText(8192);
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+ if (text.indexOf("can't store negative values") == -1) {
+ return;
+ }
+ dboxLatch.countDown();
+ }
+ }
+ };
+ try {
+ mContext.registerReceiver(receiver,
+ new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
+ am.addOnUidImportanceListener(uidListener1,
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ runShellCommand("cmd deviceidle whitelist +" + TEST_APP);
+ toggleScreenOn(true);
+
+ final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
+ final ComponentName cn = ComponentName.unflattenFromString(
+ TEST_APP + "/" + TEST_FGS_CLASS);
+ final Bundle bundle = new Bundle();
+ intent.setComponent(cn);
+ bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
+ intent.putExtras(bundle);
+
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, timeout));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(timeout);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(timeout);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_START_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for start fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ toggleScreenOn(false);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+ assertFalse("There shouldn't be negative values", dboxLatch.await(
+ timeout * 2, TimeUnit.MILLISECONDS));
+ } finally {
+ toggleScreenOn(true);
+ runShellCommand("cmd deviceidle whitelist -" + TEST_APP);
+ am.removeOnUidImportanceListener(uidListener1);
+ am.removeOnUidImportanceListener(uidListener2);
+ am.forceStopPackage(TEST_APP);
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ /**
+ * Make sure the screen state.
+ */
+ private void toggleScreenOn(final boolean screenon) throws Exception {
+ if (screenon) {
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ runShellCommand("wm dismiss-keyguard");
+ } else {
+ runShellCommand("input keyevent KEYCODE_SLEEP");
+ }
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(2_000);
+ }
+
+ private class H extends Handler {
+ static final int MSG_INIT = 0;
+ static final int MSG_DONE = 1;
+ static final int MSG_START_FOREGROUND = 2;
+ static final int MSG_STOP_FOREGROUND = 3;
+
+ private Messenger mRemoteMessenger;
+ private CountDownLatch[] mLatchHolder;
+
+ H(Looper looper, CountDownLatch[] latchHolder) {
+ super(looper);
+ mLatchHolder = latchHolder;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT:
+ mRemoteMessenger = (Messenger) msg.obj;
+ mLatchHolder[0].countDown();
+ break;
+ case MSG_DONE:
+ mLatchHolder[0].countDown();
+ break;
+ }
+ }
+
+ void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ try {
+ mRemoteMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ msg.recycle();
+ }
+ }
+
+ private static class MyUidImportanceListener implements OnUidImportanceListener {
+ final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
+ private final int mExpectedUid;
+ private int mExpectedImportance;
+ private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+ MyUidImportanceListener(int uid) {
+ mExpectedUid = uid;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mExpectedUid) {
+ synchronized (this) {
+ if (importance == mExpectedImportance && mLatchHolder[0] != null) {
+ mLatchHolder[0].countDown();
+ }
+ mCurrentImportance = importance;
+ }
+ Log.i(TAG, "uid " + uid + " importance: " + importance);
+ }
+ }
+
+ boolean waitFor(int expectedImportance, long timeout) throws Exception {
+ synchronized (this) {
+ mExpectedImportance = expectedImportance;
+ if (mCurrentImportance == expectedImportance) {
+ return true;
+ }
+ mLatchHolder[0] = new CountDownLatch(1);
+ }
+ return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index 8d54ead75761..73a2febf72aa 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -17,8 +17,10 @@
package com.android.server.am;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -39,6 +41,7 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.PowerProfile;
import org.junit.Before;
import org.junit.Test;
@@ -61,7 +64,7 @@ public class BatteryExternalStatsWorkerTest {
public void setUp() {
final Context context = InstrumentationRegistry.getContext();
- mBatteryStatsImpl = new TestBatteryStatsImpl();
+ mBatteryStatsImpl = new TestBatteryStatsImpl(context);
mPowerStatsInternal = new TestPowerStatsInternal();
mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context),
mBatteryStatsImpl);
@@ -72,13 +75,24 @@ public class BatteryExternalStatsWorkerTest {
final int numCpuClusters = 4;
final int numOther = 3;
- final IntArray tempAllIds = new IntArray();
// Add some energy consumers used by BatteryExternalStatsWorker.
+ final IntArray tempAllIds = new IntArray();
+
final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
"display");
tempAllIds.add(displayId);
mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
+ final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0,
+ "wifi");
+ tempAllIds.add(wifiId);
+ mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456);
+
+ final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0,
+ "bt");
+ tempAllIds.add(btId);
+ mPowerStatsInternal.incrementEnergyConsumption(btId, 34567);
+
final int[] cpuClusterIds = new int[numCpuClusters];
for (int i = 0; i < numCpuClusters; i++) {
cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
@@ -109,6 +123,18 @@ public class BatteryExternalStatsWorkerTest {
assertEquals(1, displayResults.length);
assertEquals(displayId, displayResults[0].id);
+ final EnergyConsumerResult[] wifiResults =
+ mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null);
+ // Results should only have the wifi energy consumer
+ assertEquals(1, wifiResults.length);
+ assertEquals(wifiId, wifiResults[0].id);
+
+ final EnergyConsumerResult[] bluetoothResults =
+ mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_BT).getNow(null);
+ // Results should only have the bluetooth energy consumer
+ assertEquals(1, bluetoothResults.length);
+ assertEquals(btId, bluetoothResults[0].id);
+
final EnergyConsumerResult[] cpuResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
// Results should only have the cpu cluster energy consumers
@@ -148,6 +174,9 @@ public class BatteryExternalStatsWorkerTest {
}
public class TestBatteryStatsImpl extends BatteryStatsImpl {
+ public TestBatteryStatsImpl(Context context) {
+ mPowerProfile = new PowerProfile(context, true /* forTest */);
+ }
}
public class TestPowerStatsInternal extends PowerStatsInternal {
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 07f67327b2bf..1c9683803857 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -40,9 +40,11 @@ import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import org.junit.Before;
@@ -60,6 +62,7 @@ import java.util.List;
* Tests for {@link com.android.server.apphibernation.AppHibernationService}
*/
@SmallTest
+@Presubmit
public final class AppHibernationServiceTest {
private static final String PACKAGE_SCHEME = "package";
private static final String PACKAGE_NAME_1 = "package1";
@@ -91,6 +94,7 @@ public final class AppHibernationServiceTest {
MockitoAnnotations.initMocks(this);
doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+ LocalServices.removeServiceForTest(AppHibernationManagerInternal.class);
mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
index 59f3c35f2137..2237c845cde7 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -19,6 +19,7 @@ package com.android.server.apphibernation;
import static org.junit.Assert.assertEquals;
import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -48,6 +49,7 @@ import java.util.concurrent.TimeoutException;
@SmallTest
+@Presubmit
public class HibernationStateDiskStoreTest {
private static final String STATES_FILE_NAME = "states";
private final MockScheduledExecutorService mMockScheduledExecutorService =
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 6890ed1688bb..c84c1cf8c8f1 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -415,9 +415,7 @@ public class AppSearchImplTest {
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
GenericDocument document =
- new GenericDocument.Builder<>("uri" + i, "type")
- .setNamespace("namespace")
- .build();
+ new GenericDocument.Builder<>("namespace", "uri" + i, "type").build();
mAppSearchImpl.putDocument("package", "database", document);
}
@@ -477,7 +475,7 @@ public class AppSearchImplTest {
// Insert document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "type").build();
mAppSearchImpl.putDocument("package", "database", document);
// Rewrite SearchSpec
@@ -517,11 +515,11 @@ public class AppSearchImplTest {
// Insert documents
GenericDocument document1 =
- new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "typeA").build();
mAppSearchImpl.putDocument("package", "database1", document1);
GenericDocument document2 =
- new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "typeB").build();
mAppSearchImpl.putDocument("package", "database2", document2);
// Rewrite SearchSpec
@@ -561,7 +559,7 @@ public class AppSearchImplTest {
// Insert document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "type").build();
mAppSearchImpl.putDocument("package", "database", document);
// If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to
@@ -614,7 +612,7 @@ public class AppSearchImplTest {
// Insert package1 document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
mAppSearchImpl.putDocument("package1", "database1", document);
// No query filters specified, package2 shouldn't be able to query for package1's documents.
@@ -625,14 +623,13 @@ public class AppSearchImplTest {
assertThat(searchResultPage.getResults()).isEmpty();
// Insert package2 document
- document =
- new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
mAppSearchImpl.putDocument("package2", "database2", document);
// No query filters specified. package2 should only get its own documents back.
searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
}
/**
@@ -665,7 +662,7 @@ public class AppSearchImplTest {
// Insert package1 document
GenericDocument document =
- new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
mAppSearchImpl.putDocument("package1", "database1", document);
// "package1" filter specified, but package2 shouldn't be able to query for package1's
@@ -680,8 +677,7 @@ public class AppSearchImplTest {
assertThat(searchResultPage.getResults()).isEmpty();
// Insert package2 document
- document =
- new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
mAppSearchImpl.putDocument("package2", "database2", document);
// "package2" filter specified, package2 should only get its own documents back.
@@ -692,7 +688,7 @@ public class AppSearchImplTest {
.build();
searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
}
@Test
@@ -1073,7 +1069,7 @@ public class AppSearchImplTest {
for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getPackageName()).isEqualTo("com.package.foo");
assertThat(result.getDatabaseName()).isEqualTo("databaseName");
- assertThat(result.getDocument())
+ assertThat(result.getGenericDocument())
.isEqualTo(
GenericDocumentToProtoConverter.toGenericDocument(
strippedDocumentProto.build()));
@@ -1128,9 +1124,7 @@ public class AppSearchImplTest {
appSearchImpl.putDocument(
"package",
"database",
- new GenericDocument.Builder<>("uri", "type")
- .setNamespace("namespace")
- .build());
+ new GenericDocument.Builder<>("namespace", "uri", "type").build());
});
expectThrows(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 194be3761903..70e1e05174ef 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -36,23 +36,23 @@ public class GenericDocumentToProtoConverterTest {
private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final GenericDocument DOCUMENT_PROPERTIES_1 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+ "namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument DOCUMENT_PROPERTIES_2 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+ "namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
@Test
public void testDocumentProtoConvert() {
GenericDocument document =
- new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1")
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setScore(1)
.setTtlMillis(1L)
- .setNamespace("namespace")
.setPropertyLong("longKey1", 1L)
.setPropertyDouble("doubleKey1", 1.0)
.setPropertyBoolean("booleanKey1", true)
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 0b1c120d8a1e..d07211fe2028 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -93,10 +93,10 @@ public class SnippetTest {
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
assertThat(match.getFullText()).isEqualTo(propertyValueString);
assertThat(match.getExactMatch()).isEqualTo(exactMatch);
- assertThat(match.getExactMatchPosition())
+ assertThat(match.getExactMatchRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32));
assertThat(match.getFullText()).isEqualTo(propertyValueString);
- assertThat(match.getSnippetPosition())
+ assertThat(match.getSnippetRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32));
assertThat(match.getSnippet()).isEqualTo(window);
}
@@ -210,20 +210,20 @@ public class SnippetTest {
SearchResult.MatchInfo match1 = result.getMatches().get(0);
assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
- assertThat(match1.getExactMatchPosition())
+ assertThat(match1.getExactMatchRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4));
assertThat(match1.getExactMatch()).isEqualTo("Test");
- assertThat(match1.getSnippetPosition())
+ assertThat(match1.getSnippetRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
assertThat(match1.getSnippet()).isEqualTo("Test Name");
SearchResult.MatchInfo match2 = result.getMatches().get(1);
assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
- assertThat(match2.getExactMatchPosition())
+ assertThat(match2.getExactMatchRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
- assertThat(match2.getSnippetPosition())
+ assertThat(match2.getSnippetRange())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
new file mode 100644
index 000000000000..7afcbf7ead1c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.SparseIntArray;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.appsearch.external.localstorage.MockPackageManager;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class PlatformLoggerTest {
+ private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
+ private static final int TEST_DEFAULT_SAMPLING_RATIO = 10;
+ private static final String TEST_PACKAGE_NAME = "packageName";
+ private MockPackageManager mMockPackageManager = new MockPackageManager();
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ mContext =
+ new ContextWrapper(context) {
+ @Override
+ public PackageManager getPackageManager() {
+ return mMockPackageManager.getMockPackageManager();
+ }
+ };
+ }
+
+ @Test
+ public void testcreateExtraStatsLocked_nullSamplingRatioMap_returnsDefaultSamplingRatio() {
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ TEST_DEFAULT_SAMPLING_RATIO,
+ /*samplingRatioMap=*/ null));
+
+ // Make sure default sampling ratio is used if samplingMap is not provided.
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_UNKNOWN).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ }
+
+
+ @Test
+ public void testcreateExtraStatsLocked_with_samplingRatioMap_returnsConfiguredSamplingRatio() {
+ int putDocumentSamplingRatio = 1;
+ int querySamplingRatio = 2;
+ final SparseIntArray samplingRatios = new SparseIntArray();
+ samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio);
+ samplingRatios.put(CallStats.CALL_TYPE_QUERY, querySamplingRatio);
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ TEST_DEFAULT_SAMPLING_RATIO,
+ samplingRatios));
+
+ // The default sampling ratio should be used if no sampling ratio is
+ // provided for certain call type.
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
+ TEST_DEFAULT_SAMPLING_RATIO);
+
+ // The configured sampling ratio is used if sampling ratio is available
+ // for certain call type.
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo(
+ putDocumentSamplingRatio);
+ assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+ CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+ querySamplingRatio);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_trueWhenSampleRatioIsOne() {
+ final int samplingRatio = 1;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ samplingRatio,
+ /* samplingMap=*/ null));
+
+ // Sample should always be logged for the first time if sampling is disabled(value is one).
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_falseWhenSampleRatioIsNegative() {
+ final int samplingRatio = -1;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ samplingRatio,
+ /* samplingMap=*/ null));
+
+ // Makes sure sample will be excluded due to sampling if sample ratio is negative.
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
+ // Skipped count should be 0 since it doesn't pass the sampling.
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() {
+ // Next sample won't be excluded due to sampling.
+ final int samplingRatio = 1;
+ // Next sample would guaranteed to be too close.
+ final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ minTimeIntervalBetweenSamplesMillis,
+ samplingRatio,
+ /* samplingMap=*/ null));
+ logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
+
+ // Makes sure sample will be excluded due to rate limiting if samples are too close.
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1);
+ }
+
+ @Test
+ public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() {
+ // Next sample won't be excluded due to sampling.
+ final int samplingRatio = 1;
+ // Next sample would guaranteed to be included.
+ final int minTimeIntervalBetweenSamplesMillis = 0;
+ final String testPackageName = "packageName";
+ PlatformLogger logger = new PlatformLogger(
+ ApplicationProvider.getApplicationContext(),
+ UserHandle.USER_NULL,
+ new PlatformLogger.Config(
+ minTimeIntervalBetweenSamplesMillis,
+ samplingRatio,
+ /* samplingMap=*/ null));
+ logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
+
+ // Makes sure sample will be logged if it is not too close to previous sample.
+ assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
+ assertThat(logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+ }
+
+ /** Makes sure the caching works while getting the UID for calling package. */
+ @Test
+ public void testGetPackageUidAsUser() throws Exception {
+ final String testPackageName = "packageName";
+ final int testUid = 1234;
+ PlatformLogger logger = new PlatformLogger(
+ mContext,
+ mContext.getUserId(),
+ new PlatformLogger.Config(
+ TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+ TEST_DEFAULT_SAMPLING_RATIO,
+ /* samplingMap=*/ null));
+ mMockPackageManager.mockGetPackageUidAsUser(testPackageName, mContext.getUserId(), testUid);
+
+ //
+ // First time, no cache
+ //
+ PlatformLogger.ExtraStats extraStats = logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+ verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
+ eq(testPackageName), /*userId=*/ anyInt());
+ assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+
+ //
+ // Second time, we have cache
+ //
+ extraStats = logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+ // Count is still one since we will use the cache
+ verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
+ eq(testPackageName), /*userId=*/ anyInt());
+ assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+
+ //
+ // Remove the cache and try again
+ //
+ assertThat(logger.removeCachedUidForPackage(testPackageName)).isEqualTo(testUid);
+ extraStats = logger.createExtraStatsLocked(testPackageName,
+ CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+ // count increased by 1 since cache is cleared
+ verify(mMockPackageManager.getMockPackageManager(), times(2)).getPackageUidAsUser(
+ eq(testPackageName), /*userId=*/ anyInt());
+ assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index f00edcc85404..fcd6b842426a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -120,6 +120,11 @@ class CompatConfigBuilder {
return this;
}
+ CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) {
+ mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
+ return this;
+ }
+
CompatConfig build() {
CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
config.forceNonDebuggableFinalForTest(false);
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index b6b6932c4a93..bd774056aef8 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -86,6 +86,7 @@ public class CompatConfigTest {
// Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+ when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
ChangeIdStateCache.disable();
when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
.thenThrow(new NameNotFoundException());
@@ -567,6 +568,34 @@ public class CompatConfigTest {
}
@Test
+ public void testReadApexConfig() throws IOException {
+ String configXml = "<config>"
+ + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+ + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+ + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+ + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />"
+ + "</config>";
+
+ File dir = createTempDir();
+ writeToFile(dir, "platform_compat_config.xml", configXml);
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
+ compatConfig.initConfigFromLib(dir);
+
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1235L,
+ ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1236L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1237L,
+ ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue();
+ }
+
+ @Test
public void testReadConfigMultipleFiles() throws IOException {
String configXml1 = "<config>"
+ "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 0fd6445fbeeb..57fdcd340a02 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import static com.google.common.truth.Truth.assertThat;
@@ -52,6 +53,7 @@ public class OverrideValidatorImplTest {
private static final int TARGET_SDK = 10;
private static final int TARGET_SDK_BEFORE = 9;
private static final int TARGET_SDK_AFTER = 11;
+ private static final int PLATFORM_SDK_VERSION = 30;
@Mock
private PackageManager mPackageManager;
@@ -61,6 +63,7 @@ public class OverrideValidatorImplTest {
private AndroidBuildClassifier debuggableBuild() {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+ when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier;
}
@@ -68,6 +71,7 @@ public class OverrideValidatorImplTest {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(false);
when(buildClassifier.isFinalBuild()).thenReturn(false);
+ when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier;
}
@@ -75,6 +79,7 @@ public class OverrideValidatorImplTest {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(false);
when(buildClassifier.isFinalBuild()).thenReturn(true);
+ when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier;
}
@@ -333,6 +338,26 @@ public class OverrideValidatorImplTest {
}
@Test
+ public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride()
+ throws Exception {
+ final AndroidBuildClassifier buildClassifier = finalBuild();
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .debuggable()
+ .build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ assertThat(stateTargetSdkLessChange).isEqualTo(
+ new OverrideAllowedState(PLATFORM_TOO_OLD, -1,
+ PLATFORM_SDK_VERSION));
+ }
+
+ @Test
public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
throws Exception {
CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 799b06734b54..3fc6e9918382 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -78,11 +78,12 @@ public class PlatformCompatTest {
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Assume userdebug/eng non-final build
mCompatConfig.forceNonDebuggableFinalForTest(false);
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+ when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
}
@@ -99,7 +100,7 @@ public class PlatformCompatTest {
.addLoggingOnlyChangeWithId(7L)
.addOverridableChangeWithId(8L)
.build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -125,8 +126,9 @@ public class PlatformCompatTest {
.addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
.addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
.addLoggingOnlyChangeWithId(7L)
+ .addEnableSinceSdkChangeWithId(31, 8L)
.build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -144,7 +146,7 @@ public class PlatformCompatTest {
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
.build();
mCompatConfig.forceNonDebuggableFinalForTest(true);
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Before adding overrides.
assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 87100a63e35e..77a39d8ac762 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -107,6 +107,7 @@ import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth
import android.util.ArraySet;
+import android.util.Log;
import android.util.Pair;
import androidx.test.filters.SmallTest;
@@ -154,6 +155,9 @@ import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
public class DevicePolicyManagerTest extends DpmTestBase {
+
+ private static final String TAG = DevicePolicyManagerTest.class.getSimpleName();
+
private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
@@ -3875,32 +3879,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
@Test
- public void testForceUpdateUserSetupComplete_userbuild() {
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
-
- final int userId = UserHandle.USER_SYSTEM;
- // GIVEN userComplete is false in SettingsProvider
- setUserSetupCompleteForUser(false, userId);
-
- // GIVEN userComplete is true in DPM
- DevicePolicyData userData = new DevicePolicyData(userId);
- userData.mUserSetupComplete = true;
- dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
-
- // GIVEN it's user build
- getServices().buildMock.isDebuggable = false;
-
- assertThat(dpms.hasUserSetupCompleted()).isTrue();
-
- dpm.forceUpdateUserSetupComplete();
-
- // THEN the state in dpms is not changed
- assertThat(dpms.hasUserSetupCompleted()).isTrue();
- }
-
- @Test
- public void testForceUpdateUserSetupComplete_userDebugbuild() {
+ public void testForceUpdateUserSetupComplete_forcesUpdate() {
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
@@ -3913,9 +3892,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
userData.mUserSetupComplete = true;
dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
- // GIVEN it's userdebug build
- getServices().buildMock.isDebuggable = true;
-
assertThat(dpms.hasUserSetupCompleted()).isTrue();
dpm.forceUpdateUserSetupComplete();
@@ -7215,6 +7191,47 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled);
}
+ @Test
+ public void testGetPolicyExemptApps_noPermission() {
+ assertThrows(SecurityException.class, () -> dpm.getPolicyExemptApps());
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_empty() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps();
+ mockVendorPolicyExemptApps();
+
+ assertThat(dpm.getPolicyExemptApps()).isEmpty();
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_baseOnly() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps("foo");
+ mockVendorPolicyExemptApps();
+
+ assertThat(dpm.getPolicyExemptApps()).containsExactly("foo");
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_vendorOnly() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps();
+ mockVendorPolicyExemptApps("bar");
+
+ assertThat(dpm.getPolicyExemptApps()).containsExactly("bar");
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_baseAndVendor() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps("4", "23", "15", "42", "8");
+ mockVendorPolicyExemptApps("16", "15", "4");
+
+ assertThat(dpm.getPolicyExemptApps()).containsExactly("4", "8", "15", "16", "23", "42");
+ }
+
private void setUserUnlocked(int userHandle, boolean unlocked) {
when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
}
@@ -7436,4 +7453,18 @@ public class DevicePolicyManagerTest extends DpmTestBase {
return new StringParceledListSlice(Arrays.asList(s));
}
+ private void grantManageDeviceAdmins() {
+ Log.d(TAG, "Granting " + permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ }
+
+ private void mockPolicyExemptApps(String... apps) {
+ Log.d(TAG, "Mocking R.array.policy_exempt_apps to return " + Arrays.toString(apps));
+ when(mContext.resources.getStringArray(R.array.policy_exempt_apps)).thenReturn(apps);
+ }
+
+ private void mockVendorPolicyExemptApps(String... apps) {
+ Log.d(TAG, "Mocking R.array.vendor_policy_exempt_apps to return " + Arrays.toString(apps));
+ when(mContext.resources.getStringArray(R.array.vendor_policy_exempt_apps)).thenReturn(apps);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index f94b800afbef..2fe2f40f34be 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -21,6 +21,11 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.admin.DeviceAdminInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.os.Parcel;
+import android.util.TypedXmlPullParser;
+import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -32,9 +37,14 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
@@ -44,18 +54,22 @@ import java.util.function.Function;
public class PolicyVersionUpgraderTest {
// NOTE: Only change this value if the corresponding CL also adds a test to test the upgrade
// to the new version.
- private static final int LATEST_TESTED_VERSION = 1;
+ private static final int LATEST_TESTED_VERSION = 2;
+ public static final String PERMISSIONS_TAG = "admin-can-grant-sensors-permissions";
+ private ComponentName mFakeAdmin;
private static class FakePolicyUpgraderDataProvider implements PolicyUpgraderDataProvider {
int mDeviceOwnerUserId;
+ ComponentName mDeviceOwnerComponent = new ComponentName("", "");
boolean mIsFileBasedEncryptionEnabled;
Map<Integer, ComponentName> mUserToComponent = new HashMap<>();
- Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo;
+ Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo = new HashMap<>();
File mDataDir;
+ int[] mUsers;
@Override
- public boolean isUserDeviceOwner(int userId) {
- return userId == mDeviceOwnerUserId;
+ public boolean isDeviceOwner(int userId, ComponentName who) {
+ return userId == mDeviceOwnerUserId && mDeviceOwnerComponent.equals(who);
}
@Override
@@ -92,6 +106,11 @@ public class PolicyVersionUpgraderTest {
public Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId) {
return componentName -> mComponentToDeviceAdminInfo.get(componentName);
}
+
+ @Override
+ public int[] getUsersForUpgrade() {
+ return mUsers;
+ }
}
private final Context mRealTestContext = InstrumentationRegistry.getTargetContext();
@@ -105,38 +124,69 @@ public class PolicyVersionUpgraderTest {
mUpgrader = new PolicyVersionUpgrader(mProvider);
mDataDir = new File(mRealTestContext.getCacheDir(), "test-data");
mDataDir.getParentFile().mkdirs();
+ // Prepare provider.
mProvider.mDataDir = mDataDir;
+ mFakeAdmin = new ComponentName(
+ "com.android.frameworks.servicestests",
+ "com.android.server.devicepolicy.DummyDeviceAdmins$Admin1");
+ ActivityInfo activityInfo = createActivityInfo(mFakeAdmin);
+ DeviceAdminInfo dai = createDeviceAdminInfo(activityInfo);
+ mProvider.mComponentToDeviceAdminInfo.put(mFakeAdmin, dai);
+ mProvider.mUsers = new int[] {0};
}
@Test
public void testSameVersionDoesNothing() throws IOException {
- int[] users = new int[] {0};
writeVersionToXml(DevicePolicyManagerService.DPMS_VERSION);
- preparePoliciesFile(users[0]);
- String oldContents = readPoliciesFile(0);
+ final int userId = mProvider.mUsers[0];
+ preparePoliciesFile(userId);
+ String oldContents = readPoliciesFile(userId);
- mUpgrader.upgradePolicy(users, DevicePolicyManagerService.DPMS_VERSION);
+ mUpgrader.upgradePolicy(DevicePolicyManagerService.DPMS_VERSION);
String newContents = readPoliciesFile(0);
assertThat(newContents).isEqualTo(oldContents);
}
@Test
- public void testUpgrade0To1RemovesPasswordMetrics() throws IOException {
- int[] users = new int[] {0, 10};
+ public void testUpgrade0To1RemovesPasswordMetrics() throws IOException, XmlPullParserException {
+ final String activePasswordTag = "active-password";
+ mProvider.mUsers = new int[] {0, 10};
writeVersionToXml(0);
- for (int userId : users) {
+ for (int userId : mProvider.mUsers) {
preparePoliciesFile(userId);
}
+ // Validate test set-up.
+ assertThat(isTagPresent(readPoliciesFileToStream(0), activePasswordTag)).isTrue();
+
+ mUpgrader.upgradePolicy(1);
+
+ assertThat(readVersionFromXml()).isGreaterThan(1);
+ for (int user: mProvider.mUsers) {
+ assertThat(isTagPresent(readPoliciesFileToStream(user), activePasswordTag)).isFalse();
+ }
+ }
- String oldContents = readPoliciesFile(0);
- assertThat(oldContents).contains("active-password");
+ @Test
+ public void testUpgrade1To2MarksDoForPermissionControl()
+ throws IOException, XmlPullParserException {
+ final int ownerUser = 10;
+ mProvider.mUsers = new int[] {0, ownerUser};
+ writeVersionToXml(1);
+ for (int userId : mProvider.mUsers) {
+ preparePoliciesFile(userId);
+ }
+ mProvider.mDeviceOwnerUserId = ownerUser;
+ mProvider.mDeviceOwnerComponent = mFakeAdmin;
+ mProvider.mUserToComponent.put(ownerUser, mFakeAdmin);
- mUpgrader.upgradePolicy(users, 1);
+ mUpgrader.upgradePolicy(2);
- assertThat(readVersionFromXml()).isEqualTo(1);
- assertThat(readPoliciesFile(users[0])).doesNotContain("active-password");
- assertThat(readPoliciesFile(users[1])).doesNotContain("active-password");
+ assertThat(readVersionFromXml()).isEqualTo(2);
+ assertThat(getBooleanValueTag(readPoliciesFileToStream(mProvider.mUsers[0]),
+ PERMISSIONS_TAG)).isFalse();
+ assertThat(getBooleanValueTag(readPoliciesFileToStream(ownerUser),
+ PERMISSIONS_TAG)).isTrue();
}
@Test
@@ -169,6 +219,70 @@ public class PolicyVersionUpgraderTest {
private String readPoliciesFile(int userId) throws IOException {
File policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId).chooseForRead();
- return new String(Files.asByteSource(policiesFile).read());
+ FileReader reader = new FileReader(policiesFile);
+ return new String(Files.asByteSource(policiesFile).read(), Charset.defaultCharset());
+ }
+
+ private InputStream readPoliciesFileToStream(int userId) throws IOException {
+ File policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId).chooseForRead();
+ return new FileInputStream(policiesFile);
+ }
+
+ private boolean getBooleanValueTag(InputStream inputXml, String tagName)
+ throws IOException, XmlPullParserException {
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputXml);
+
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if (tagName.equals(tag)) {
+ String res = parser.getAttributeValue(null, "value");
+ return Boolean.parseBoolean(res);
+ }
+ }
+ eventType = parser.next();
+ }
+
+ throw new IllegalStateException("Could not find " + tagName);
+ }
+
+ private boolean isTagPresent(InputStream inputXml, String tagName)
+ throws IOException, XmlPullParserException {
+ TypedXmlPullParser parser = Xml.resolvePullParser(inputXml);
+
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if (tagName.equals(tag)) {
+ return true;
+ }
+ }
+ eventType = parser.next();
+ }
+
+ return false;
+ }
+
+ private ActivityInfo createActivityInfo(ComponentName admin) {
+ ActivityInfo ai = new ActivityInfo();
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.className = admin.getClassName();
+ applicationInfo.uid = 2222;
+ ai.applicationInfo = applicationInfo;
+ ai.name = admin.getClassName();
+ ai.packageName = admin.getPackageName();
+ return ai;
+ }
+
+ private DeviceAdminInfo createDeviceAdminInfo(ActivityInfo activityInfo) {
+ Parcel parcel = Parcel.obtain();
+ activityInfo.writeToParcel(parcel, 0);
+ parcel.writeInt(0);
+ parcel.writeBoolean(true);
+ parcel.setDataPosition(0);
+
+ return DeviceAdminInfo.CREATOR.createFromParcel(parcel);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 54825ee2745a..bf621b1f56cb 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -30,11 +30,13 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,6 +45,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessControllerTest {
private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
@@ -55,9 +58,11 @@ public class AutomaticBrightnessControllerTest {
private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
+ private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
private Context mContext;
private LogicalDisplay mLogicalDisplay;
+ private AutomaticBrightnessController mController;
@Mock SensorManager mSensorManager;
@Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
@@ -65,8 +70,8 @@ public class AutomaticBrightnessControllerTest {
@Mock HysteresisLevels mScreenBrightnessThresholds;
@Mock Handler mNoOpHandler;
@Mock DisplayDevice mDisplayDevice;
+ @Mock HighBrightnessModeController mHbmController;
- private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
@Before
public void setUp() {
// Share classloader to allow package private access.
@@ -77,6 +82,15 @@ public class AutomaticBrightnessControllerTest {
mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
}
+ @After
+ public void tearDown() {
+ if (mController != null) {
+ // Stop the update Brightness loop.
+ mController.stop();
+ mController = null;
+ }
+ }
+
private AutomaticBrightnessController setupController(Sensor lightSensor) {
AutomaticBrightnessController controller = new AutomaticBrightnessController(
new AutomaticBrightnessController.Injector() {
@@ -90,9 +104,12 @@ public class AutomaticBrightnessControllerTest {
BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
- mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay, mContext
+ mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay,
+ mContext, mHbmController
);
- controller.setLoggingEnabled(true);
+
+ when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT);
// Configure the brightness controller and grab an instance of the sensor listener,
// through which we can deliver fake (for test) sensor values.
@@ -106,7 +123,7 @@ public class AutomaticBrightnessControllerTest {
@Test
public void testNoHysteresisAtMinBrightness() throws Exception {
Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
- AutomaticBrightnessController controller = setupController(lightSensor);
+ mController = setupController(lightSensor);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -133,7 +150,7 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
- assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f);
// Set up system to return 0.0f (minimum possible brightness) as a brightness value
float lux2 = 10.0f;
@@ -147,13 +164,13 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
- assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f);
}
@Test
public void testNoHysteresisAtMaxBrightness() throws Exception {
Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
- AutomaticBrightnessController controller = setupController(lightSensor);
+ mController = setupController(lightSensor);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -179,7 +196,7 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
- assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f);
// Set up system to return 1.0f as a brightness value (brightness_max)
@@ -194,13 +211,13 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
- assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f);
}
@Test
public void testUserAddUserDataPoint() throws Exception {
Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
- AutomaticBrightnessController controller = setupController(lightSensor);
+ mController = setupController(lightSensor);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -212,7 +229,7 @@ public class AutomaticBrightnessControllerTest {
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
// User sets brightness to 100
- controller.configure(true /* enable */, null /* configuration */,
+ mController.configure(true /* enable */, null /* configuration */,
0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 893ce9e6c70c..bdf94f3a2882 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -81,6 +81,7 @@ import java.util.concurrent.TimeUnit;
public class BrightnessTrackerTest {
private static final float DEFAULT_INITIAL_BRIGHTNESS = 2.5f;
private static final boolean DEFAULT_COLOR_SAMPLING_ENABLED = true;
+ private static final String DEFAULT_DISPLAY_ID = "123";
private static final float FLOAT_DELTA = 0.01f;
private BrightnessTracker mTracker;
@@ -285,18 +286,20 @@ public class BrightnessTrackerTest {
@Test
public void testBrightnessEvent() {
- final int brightness = 20;
+ final float brightness = 0.5f;
+ final String displayId = "1234";
startTracker(mTracker);
mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
- notifyBrightnessChanged(mTracker, brightness);
+ notifyBrightnessChanged(mTracker, brightness, displayId);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
mTracker.stop();
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
+ assertEquals(displayId, event.uniqueDisplayId);
assertEquals(1, event.luxValues.length);
assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA);
assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2),
@@ -314,6 +317,7 @@ public class BrightnessTrackerTest {
public void testBrightnessFullPopulatedEvent() {
final int initialBrightness = 230;
final int brightness = 130;
+ final String displayId = "1234";
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333);
@@ -326,7 +330,7 @@ public class BrightnessTrackerTest {
batteryChangeEvent(30, 60));
mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
final long sensorTime = mInjector.currentTimeMillis();
- notifyBrightnessChanged(mTracker, brightness);
+ notifyBrightnessChanged(mTracker, brightness, displayId);
List<BrightnessChangeEvent> eventsNoPackage
= mTracker.getEvents(0, false).getList();
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
@@ -335,6 +339,7 @@ public class BrightnessTrackerTest {
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
assertEquals(event.timeStamp, mInjector.currentTimeMillis());
+ assertEquals(displayId, event.uniqueDisplayId);
assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f);
assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
@@ -364,7 +369,7 @@ public class BrightnessTrackerTest {
final int systemUpdatedBrightness = 20;
notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/,
0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
// No events because we filtered out our change.
assertEquals(0, events.size());
@@ -455,6 +460,7 @@ public class BrightnessTrackerTest {
+ "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\" "
+ "reduceBrightColors=\"false\" reduceBrightColorsStrength=\"40\" "
+ "reduceBrightColorsOffset=\"0\"\n"
+ + "uniqueDisplayId=\"123\""
+ "lux=\"32.2,31.1\" luxTimestamps=\""
+ Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />"
@@ -465,6 +471,7 @@ public class BrightnessTrackerTest {
+ "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\" "
+ "reduceBrightColors=\"true\" reduceBrightColorsStrength=\"40\" "
+ "reduceBrightColorsOffset=\"0\"\n"
+ + "uniqueDisplayId=\"456\""
+ "lux=\"132.2,131.1\" luxTimestamps=\""
+ Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ "colorSampleDuration=\"3456\" colorValueBuckets=\"123,598,23,19\"/>"
@@ -476,6 +483,7 @@ public class BrightnessTrackerTest {
+ "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\" "
+ "reduceBrightColors=\"false\" reduceBrightColorsStrength=\"40\" "
+ "reduceBrightColorsOffset=\"0\"\n"
+ + "uniqueDisplayId=\"789\""
+ "lux=\"32.2,31.1\" luxTimestamps=\""
+ Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>"
+ "</events>";
@@ -485,6 +493,7 @@ public class BrightnessTrackerTest {
BrightnessChangeEvent event = events.get(0);
assertEquals(someTimeAgo, event.timeStamp);
assertEquals(194.2, event.brightness, FLOAT_DELTA);
+ assertEquals("123", event.uniqueDisplayId);
assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, FLOAT_DELTA);
assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
assertEquals(32.333, event.lastBrightness, FLOAT_DELTA);
@@ -503,6 +512,7 @@ public class BrightnessTrackerTest {
event = events.get(0);
assertEquals(someTimeAgo, event.timeStamp);
assertEquals(71, event.brightness, FLOAT_DELTA);
+ assertEquals("456", event.uniqueDisplayId);
assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, FLOAT_DELTA);
assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
assertEquals(32, event.lastBrightness, FLOAT_DELTA);
@@ -575,6 +585,7 @@ public class BrightnessTrackerTest {
@Test
public void testWriteThenRead() throws Exception {
final int brightness = 20;
+ final String displayId = "1234";
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
@@ -593,7 +604,7 @@ public class BrightnessTrackerTest {
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
0.5f /*powerBrightnessFactor*/, true /*hasUserBrightnessPoints*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, displayId);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mTracker.writeEventsLocked(baos);
mTracker.stop();
@@ -607,6 +618,7 @@ public class BrightnessTrackerTest {
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
+ assertEquals(displayId, event.uniqueDisplayId);
assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA);
assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
@@ -678,6 +690,7 @@ public class BrightnessTrackerTest {
builder.setTimeStamp(345L);
builder.setPackageName("com.example");
builder.setUserId(12);
+ builder.setUniqueDisplayId("9876");
float[] luxValues = new float[2];
luxValues[0] = 3000.0f;
luxValues[1] = 4000.0f;
@@ -710,6 +723,7 @@ public class BrightnessTrackerTest {
assertEquals(event.timeStamp, event2.timeStamp);
assertEquals(event.packageName, event2.packageName);
assertEquals(event.userId, event2.userId);
+ assertEquals(event.uniqueDisplayId, event2.uniqueDisplayId);
assertArrayEquals(event.luxValues, event2.luxValues, FLOAT_DELTA);
assertArrayEquals(event.luxTimestamps, event2.luxTimestamps);
assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
@@ -773,7 +787,7 @@ public class BrightnessTrackerTest {
long eventTime = mInjector.currentTimeMillis();
mTracker.notifyBrightnessChanged(brightness, true /*userInitiated*/,
1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID);
// Time passes before handler can run.
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
@@ -791,6 +805,35 @@ public class BrightnessTrackerTest {
assertEquals(eventTime, event.timeStamp);
}
+ @Test
+ public void testDisplayIdChange() {
+ float firstBrightness = 0.5f;
+ float secondBrightness = 0.75f;
+ String firstDisplayId = "123";
+ String secondDisplayId = "456";
+
+ startTracker(mTracker);
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
+
+ notifyBrightnessChanged(mTracker, firstBrightness, firstDisplayId);
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
+ assertEquals(1, events.size());
+ BrightnessChangeEvent firstEvent = events.get(0);
+ assertEquals(firstDisplayId, firstEvent.uniqueDisplayId);
+ assertEquals(firstBrightness, firstEvent.brightness, 0.001f);
+
+ notifyBrightnessChanged(mTracker, secondBrightness, secondDisplayId);
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ events = mTracker.getEvents(0, true).getList();
+ assertEquals(2, events.size());
+ BrightnessChangeEvent secondEvent = events.get(1);
+ assertEquals(secondDisplayId, secondEvent.uniqueDisplayId);
+ assertEquals(secondBrightness, secondEvent.brightness, 0.001f);
+
+ mTracker.stop();
+ }
+
private InputStream getInputStream(String data) {
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
}
@@ -831,16 +874,21 @@ public class BrightnessTrackerTest {
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) {
+ notifyBrightnessChanged(tracker, brightness, DEFAULT_DISPLAY_ID);
+ }
+
+ private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
+ String displayId) {
notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/,
1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/);
+ false /*isDefaultBrightnessConfig*/, displayId);
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig) {
+ boolean isDefaultBrightnessConfig, String displayId) {
tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
- isUserSetBrightness, isDefaultBrightnessConfig);
+ isUserSetBrightness, isDefaultBrightnessConfig, displayId);
mInjector.waitForHandler();
}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 843296e31800..dbb415c88a5f 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -68,7 +68,7 @@ public final class UpdatableFontDirTest {
*/
private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser {
@Override
- public String getPostScriptName(File file) throws IOException {
+ public String getCanonicalFileName(File file) throws IOException {
String content = FileUtils.readTextFile(file, 100, "");
return content.split(",")[0];
}
@@ -160,10 +160,10 @@ public final class UpdatableFontDirTest {
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
.isEqualTo(expectedModifiedDate);
dirForPreparation.update(Arrays.asList(
- newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
- newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='foobar'>"
+ " <font>foo.ttf</font>"
+ " <font>bar.ttf</font>"
@@ -214,10 +214,10 @@ public final class UpdatableFontDirTest {
mConfigFile);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
- newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
- newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='foobar'>"
+ " <font>foo.ttf</font>"
+ " <font>bar.ttf</font>"
@@ -246,10 +246,10 @@ public final class UpdatableFontDirTest {
mConfigFile);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
- newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
- newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='foobar'>"
+ " <font>foo.ttf</font>"
+ " <font>bar.ttf</font>"
@@ -279,10 +279,10 @@ public final class UpdatableFontDirTest {
mConfigFile);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
- newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
- newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='foobar'>"
+ " <font>foo.ttf</font>"
+ " <font>bar.ttf</font>"
@@ -332,14 +332,14 @@ public final class UpdatableFontDirTest {
mConfigFile);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
- newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='foobar'>"
+ " <font>foo.ttf</font>"
+ "</family>")));
try {
dirForPreparation.update(Arrays.asList(
- newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", "Invalid signature"),
+ newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", "Invalid signature"),
newAddFontFamilyRequest("<family name='foobar'>"
+ " <font>foo.ttf</font>"
+ " <font>bar.ttf</font>"
@@ -372,7 +372,7 @@ public final class UpdatableFontDirTest {
mConfigFile);
dir.loadFontFileMap();
- dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
File fontFile = dir.getFontFileMap().get("test.ttf");
@@ -390,9 +390,9 @@ public final class UpdatableFontDirTest {
mConfigFile);
dir.loadFontFileMap();
- dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE)));
Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
- dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
assertThat(mapBeforeUpgrade).containsKey("test.ttf");
@@ -409,9 +409,10 @@ public final class UpdatableFontDirTest {
mConfigFile);
dir.loadFontFileMap();
- dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE)));
try {
- dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+ GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -430,8 +431,8 @@ public final class UpdatableFontDirTest {
mConfigFile);
dir.loadFontFileMap();
- dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
- dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -448,8 +449,8 @@ public final class UpdatableFontDirTest {
dir.loadFontFileMap();
dir.update(Arrays.asList(
- newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+ newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -467,7 +468,8 @@ public final class UpdatableFontDirTest {
try {
dir.update(
- Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature")));
+ Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+ "Invalid signature")));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -480,14 +482,15 @@ public final class UpdatableFontDirTest {
public void installFontFile_olderThanPreinstalledFont() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
- FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+ FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
dir.loadFontFileMap();
try {
- dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+ GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -500,7 +503,7 @@ public final class UpdatableFontDirTest {
long expectedModifiedDate = 1234567890;
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
- FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+ FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1");
File readonlyDir = new File(mCacheDir, "readonly");
assertThat(readonlyDir.mkdir()).isTrue();
@@ -519,7 +522,8 @@ public final class UpdatableFontDirTest {
try {
dir.update(
- Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+ Collections.singletonList(newFontUpdateRequest("test.ttf,2",
+ GOOD_SIGNATURE)));
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
.isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG);
@@ -539,7 +543,7 @@ public final class UpdatableFontDirTest {
mUpdatableFontFilesDir, mPreinstalledFontDirs,
new UpdatableFontDir.FontFileParser() {
@Override
- public String getPostScriptName(File file) throws IOException {
+ public String getCanonicalFileName(File file) throws IOException {
return null;
}
@@ -551,7 +555,8 @@ public final class UpdatableFontDirTest {
dir.loadFontFileMap();
try {
- dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+ GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -567,7 +572,7 @@ public final class UpdatableFontDirTest {
mUpdatableFontFilesDir, mPreinstalledFontDirs,
new UpdatableFontDir.FontFileParser() {
@Override
- public String getPostScriptName(File file) throws IOException {
+ public String getCanonicalFileName(File file) throws IOException {
throw new IOException();
}
@@ -579,7 +584,8 @@ public final class UpdatableFontDirTest {
dir.loadFontFileMap();
try {
- dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+ GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -615,7 +621,8 @@ public final class UpdatableFontDirTest {
dir.loadFontFileMap();
try {
- dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+ GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -633,11 +640,11 @@ public final class UpdatableFontDirTest {
mConfigFile);
dir.loadFontFileMap();
- dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE)));
try {
dir.update(Arrays.asList(
- newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,2", "Invalid signature")));
+ newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar.ttf,2", "Invalid signature")));
fail("Batch update with invalid signature should fail");
} catch (FontManagerService.SystemFontException e) {
// Expected
@@ -657,7 +664,7 @@ public final class UpdatableFontDirTest {
dir.loadFontFileMap();
dir.update(Arrays.asList(
- newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='test'>"
+ " <font>test.ttf</font>"
+ "</family>")));
@@ -680,7 +687,7 @@ public final class UpdatableFontDirTest {
try {
dir.update(Arrays.asList(
- newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family lang='en'>"
+ " <font>test.ttf</font>"
+ "</family>")));
@@ -722,7 +729,7 @@ public final class UpdatableFontDirTest {
assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace");
dir.update(Arrays.asList(
- newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
// Updating an existing font family.
newAddFontFamilyRequest("<family name='monospace'>"
+ " <font>test.ttf</font>"
@@ -755,7 +762,7 @@ public final class UpdatableFontDirTest {
assertThat(firstFontFamily.getName()).isNotEmpty();
dir.update(Arrays.asList(
- newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>"
+ " <font>test.ttf</font>"
+ "</family>")));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 137bd88b1489..375704ee31bf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -16,183 +16,206 @@
package com.android.server.hdmi;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
import android.annotation.NonNull;
import android.content.Context;
-import android.util.Slog;
-
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.XmlParser;
-
-import org.xmlpull.v1.XmlPullParserException;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.datatype.DatatypeConfigurationException;
+import com.android.internal.R;
/**
- * Fake class which loads default system configuration with user-configurable
+ * Fake class which stubs default system configuration with user-configurable
* settings (useful for testing).
*/
final class FakeHdmiCecConfig extends HdmiCecConfig {
private static final String TAG = "FakeHdmiCecConfig";
- private static final String SYSTEM_CONFIG_XML =
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + " <setting name=\"power_state_change_on_active_source_lost\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"none\" />"
- + " <value string-value=\"standby_now\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"none\" />"
- + " </setting>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"hdmi_cec_version\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0x05\" />"
- + " <value int-value=\"0x06\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0x05\" />"
- + " </setting>"
- + " <setting name=\"system_audio_mode_muting\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"volume_control_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"tv_wake_on_one_touch_play\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"tv_send_standby_on_sleep\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"rc_profile_tv\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0x0\" />"
- + " <value int-value=\"0x2\" />"
- + " <value int-value=\"0x6\" />"
- + " <value int-value=\"0xA\" />"
- + " <value int-value=\"0xE\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0x0\" />"
- + " </setting>"
- + " <setting name=\"rc_profile_source_handles_root_menu\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"rc_profile_source_handles_setup_menu\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"rc_profile_source_handles_contents_menu\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0\" />"
- + " </setting>"
- + " <setting name=\"rc_profile_source_handles_top_menu\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0\" />"
- + " </setting>"
- + " <setting name=\"rc_profile_source_handles_media_context_sensitive_menu\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0\" />"
- + " </setting>"
- + "</cec-settings>";
+ public static Context buildContext(Context context) {
+ Context contextSpy = spy(new ContextWrapper(context));
+ doReturn(buildResources(context)).when(contextSpy).getResources();
+ return contextSpy;
+ }
- FakeHdmiCecConfig(@NonNull Context context) {
- super(context, new StorageAdapter(context), parseFromString(SYSTEM_CONFIG_XML), null);
+ private static Resources buildResources(Context context) {
+ Resources resources = spy(context.getResources());
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecEnabled_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecControlEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecControlDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecVersion_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecVersion14b_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecVersion14b_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecVersion20_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecHdmiCecVersion20_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSendStandbyOnSleep_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTv_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTv_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeBroadcast_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeBroadcast_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeNone_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeNone_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioModeMuting_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioModeMutingEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioModeMutingDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioModeMutingDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecVolumeControlMode_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecVolumeControlModeEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecVolumeControlModeEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecVolumeControlModeDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecVolumeControlModeDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTv_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvNone_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvNone_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvOne_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvOne_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvTwo_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvTwo_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvThree_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvThree_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvFour_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileTvFour_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceRootMenuHandled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceRootMenuHandled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceTopMenuHandled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceTopMenuHandled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
+
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
+
+ return resources;
}
- private static CecSettings parseFromString(@NonNull String configXml) {
- CecSettings config = null;
- try {
- config = XmlParser.read(
- new ByteArrayInputStream(configXml.getBytes()));
- } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
- Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
- }
- return config;
+ FakeHdmiCecConfig(@NonNull Context context) {
+ super(buildContext(context), new StorageAdapter(context));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index 798cf85957c0..c834510ba24c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@ import static org.testng.Assert.assertThrows;
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.hdmi.HdmiControlManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -35,6 +37,8 @@ import android.provider.Settings.Global;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -61,265 +65,118 @@ public final class HdmiCecConfigTest {
@Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
@Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener;
+ private void setBooleanResource(int resId, boolean value) {
+ Resources resources = mContext.getResources();
+ doReturn(value).when(resources).getBoolean(resId);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getTargetContext();
- }
-
- @Test
- public void getAllCecSettings_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
- }
-
- @Test
- public void getAllCecSettings_Empty() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
- assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+ mContext = FakeHdmiCecConfig.buildContext(InstrumentationRegistry.getTargetContext());
}
@Test
public void getAllCecSettings_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getAllSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- }
-
- @Test
- public void getUserCecSettings_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
- }
-
- @Test
- public void getUserCecSettings_Empty() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
- assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
- }
-
- @Test
- public void getUserCecSettings_OnlyMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+ HdmiControlManager
+ .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
+ }
+
+ @Test
+ public void getUserCecSettings_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+ HdmiControlManager
+ .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
}
@Test
public void getUserCecSettings_WithOverride() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>",
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>");
+ setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getUserSettings())
- .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
- }
-
- @Test
- public void isStringValueType_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.isStringValueType("foo"));
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+ HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+ HdmiControlManager
+ .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
}
@Test
public void isStringValueType_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.isStringValueType("foo"));
}
@Test
public void isStringValueType_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertTrue(hdmiCecConfig.isStringValueType(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
}
@Test
- public void isIntValueType_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.isIntValueType("foo"));
- }
-
- @Test
public void isIntValueType_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.isIntValueType("foo"));
}
@Test
public void isIntValueType_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertTrue(hdmiCecConfig.isIntValueType(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
}
@Test
- public void getAllowedStringValues_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getAllowedStringValues("foo"));
- }
-
- @Test
public void getAllowedStringValues_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getAllowedStringValues("foo"));
}
@Test
public void getAllowedStringValues_InvalidValueType() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -327,21 +184,7 @@ public final class HdmiCecConfigTest {
@Test
public void getAllowedStringValues_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
@@ -350,41 +193,25 @@ public final class HdmiCecConfigTest {
}
@Test
- public void getAllowedIntValues_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getAllowedIntValues("foo"));
+ public void getAllowedStringValues_WithOverride() {
+ setBooleanResource(R.bool.config_cecPowerControlModeNone_allowed, false);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+ assertThat(hdmiCecConfig.getAllowedStringValues(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+ .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@Test
public void getAllowedIntValues_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getAllowedIntValues("foo"));
}
@Test
public void getAllowedIntValues_InvalidValueType() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getAllowedIntValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -392,20 +219,7 @@ public final class HdmiCecConfigTest {
@Test
public void getAllowedIntValues_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getAllowedIntValues(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
.containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
@@ -413,62 +227,24 @@ public final class HdmiCecConfigTest {
}
@Test
- public void getAllowedIntValues_HexValues() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0x00\" />"
- + " <value int-value=\"0x01\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0x01\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ public void getAllowedIntValues_WithOverride() {
+ setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_allowed, false);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getAllowedIntValues(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
- .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
- HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
- }
-
- @Test
- public void getDefaultStringValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getDefaultStringValue("foo"));
+ .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
}
@Test
public void getDefaultStringValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getDefaultStringValue("foo"));
}
@Test
public void getDefaultStringValue_InvalidValueType() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getDefaultStringValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -476,62 +252,46 @@ public final class HdmiCecConfigTest {
@Test
public void getDefaultStringValue_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
}
@Test
- public void getDefaultIntValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getDefaultIntValue("foo"));
+ public void getDefaultStringValue_WithOverride() {
+ setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+ assertThat(hdmiCecConfig.getDefaultStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+ }
+
+ @Test
+ public void getDefaultStringValue_MultipleDefaults() {
+ setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
+ assertThrows(RuntimeException.class,
+ () -> new HdmiCecConfig(mContext, mStorageAdapter));
+ }
+
+ @Test
+ public void getDefaultStringValue_NoDefault() {
+ setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ assertThrows(RuntimeException.class,
+ () -> new HdmiCecConfig(mContext, mStorageAdapter));
}
@Test
public void getDefaultIntValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getDefaultIntValue("foo"));
}
@Test
public void getDefaultIntValue_InvalidValueType() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getDefaultIntValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -539,81 +299,32 @@ public final class HdmiCecConfigTest {
@Test
public void getDefaultIntValue_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
.isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
}
@Test
- public void getDefaultIntValue_HexValue() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0x00\" />"
- + " <value int-value=\"0x01\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0x01\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ public void getDefaultIntValue_WithOverride() {
+ setBooleanResource(R.bool.config_cecHdmiCecControlEnabled_default, false);
+ setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_default, true);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
- .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
- }
-
- @Test
- public void getStringValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getStringValue("foo"));
+ .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
}
@Test
public void getStringValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getStringValue("foo"));
}
@Test
public void getStringValue_InvalidType() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getStringValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -625,21 +336,7 @@ public final class HdmiCecConfigTest {
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
HdmiControlManager.POWER_CONTROL_MODE_TV))
.thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -652,61 +349,22 @@ public final class HdmiCecConfigTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE))
.thenReturn(
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"power_state_change_on_active_source_lost\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"none\" />"
- + " <value string-value=\"standby_now\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"none\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
.isEqualTo(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
}
@Test
- public void getIntValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getIntValue("foo"));
- }
-
- @Test
public void getIntValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getIntValue("foo"));
}
@Test
public void getIntValue_InvalidType() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getIntValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -718,45 +376,7 @@ public final class HdmiCecConfigTest {
Global.HDMI_CONTROL_ENABLED,
Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
.thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
- assertThat(hdmiCecConfig.getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
- .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
- }
-
- @Test
- public void getIntValue_GlobalSetting_HexValue() {
- when(mStorageAdapter.retrieveGlobalSetting(
- Global.HDMI_CONTROL_ENABLED,
- Integer.toHexString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
- .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0x0\" />"
- + " <value int-value=\"0x1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0x1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
.isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
@@ -768,61 +388,23 @@ public final class HdmiCecConfigTest {
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED)))
.thenReturn(Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED));
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"system_audio_mode_muting\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getIntValue(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING))
.isEqualTo(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
}
@Test
- public void setStringValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.setStringValue("foo", "bar"));
- }
-
- @Test
public void setStringValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setStringValue("foo", "bar"));
}
@Test
public void setStringValue_NotConfigurable() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ setBooleanResource(R.bool.config_cecSendStandbyOnSleep_userConfigurable, false);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -831,21 +413,7 @@ public final class HdmiCecConfigTest {
@Test
public void setStringValue_InvalidValue() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -854,21 +422,7 @@ public final class HdmiCecConfigTest {
@Test
public void setStringValue_GlobalSetting_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"send_standby_on_sleep\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"to_tv\" />"
- + " <value string-value=\"broadcast\" />"
- + " <value string-value=\"none\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"to_tv\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
verify(mStorageAdapter).storeGlobalSetting(
@@ -878,20 +432,7 @@ public final class HdmiCecConfigTest {
@Test
public void setStringValue_SharedPref_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"power_state_change_on_active_source_lost\""
- + " value-type=\"string\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value string-value=\"none\" />"
- + " <value string-value=\"standby_now\" />"
- + " </allowed-values>"
- + " <default-value string-value=\"none\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -901,40 +442,16 @@ public final class HdmiCecConfigTest {
}
@Test
- public void setIntValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter, null, null);
- assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.setIntValue("foo", 0));
- }
-
- @Test
public void setIntValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setIntValue("foo", 0));
}
@Test
public void setIntValue_NotConfigurable() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"false\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -943,20 +460,7 @@ public final class HdmiCecConfigTest {
@Test
public void setIntValue_InvalidValue() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -965,43 +469,7 @@ public final class HdmiCecConfigTest {
@Test
public void setIntValue_GlobalSetting_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
- hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
- verify(mStorageAdapter).storeGlobalSetting(
- Global.HDMI_CONTROL_ENABLED,
- Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
- }
-
- @Test
- public void setIntValue_GlobalSetting_HexValue() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0x0\" />"
- + " <value int-value=\"0x1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"0x1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
verify(mStorageAdapter).storeGlobalSetting(
@@ -1011,20 +479,7 @@ public final class HdmiCecConfigTest {
@Test
public void setIntValue_SharedPref_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"system_audio_mode_muting\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.setIntValue(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
@@ -1035,20 +490,7 @@ public final class HdmiCecConfigTest {
@Test
public void registerChangeListener_SharedPref_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"system_audio_mode_muting\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.registerChangeListener(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
mSettingChangeListener);
@@ -1061,20 +503,7 @@ public final class HdmiCecConfigTest {
@Test
public void removeChangeListener_SharedPref_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"system_audio_mode_muting\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.registerChangeListener(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
mSettingChangeListener);
@@ -1100,20 +529,7 @@ public final class HdmiCecConfigTest {
String originalValue = Global.getString(mContext.getContentResolver(),
Global.HDMI_CONTROL_ENABLED);
try {
- HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
- mContext, mStorageAdapter,
- "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<cec-settings>"
- + " <setting name=\"hdmi_cec_enabled\""
- + " value-type=\"int\""
- + " user-configurable=\"true\">"
- + " <allowed-values>"
- + " <value int-value=\"0\" />"
- + " <value int-value=\"1\" />"
- + " </allowed-values>"
- + " <default-value int-value=\"1\" />"
- + " </setting>"
- + "</cec-settings>", null);
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper());
HdmiCecConfig.SettingChangeListener latchUpdateListener =
new HdmiCecConfig.SettingChangeListener() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 915392e6eb80..1a6bad8b29cf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -61,6 +61,12 @@ import java.util.concurrent.TimeUnit;
/** Tests for {@link HdmiCecLocalDevicePlayback} class. */
public class HdmiCecLocalDevicePlaybackTest {
+ private static final int PORT_1 = 1;
+ private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
+ ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
+ 0x1234, "TV",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
@@ -159,6 +165,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
mTestLooper.dispatchAll();
mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
mNativeWrapper.clearResultMessages();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 47f3bf9daba7..b5336e3fd807 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -45,7 +45,6 @@ import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -667,7 +666,6 @@ public class HdmiControlServiceTest {
@Test
public void initCecVersion_limitToMinimumSupportedVersion() {
mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
- Log.e("MARVIN", "set setting CEC");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
@@ -679,9 +677,7 @@ public class HdmiControlServiceTest {
@Test
public void initCecVersion_limitToAtLeast1_4() {
- Log.e("MARVIN", "set HAL CEC to 0");
mNativeWrapper.setCecVersion(0x0);
- Log.e("MARVIN", "set setting CEC to 2");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
@@ -694,7 +690,6 @@ public class HdmiControlServiceTest {
@Test
public void initCecVersion_useHighestMatchingVersion() {
mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
- Log.e("MARVIN", "set setting CEC");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
new file mode 100644
index 000000000000..c61635cbd4b6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.OneTouchPlayAction.STATE_WAITING_FOR_REPORT_POWER_STATUS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/** Tests for {@link OneTouchPlayAction} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class OneTouchPlayActionTest {
+ private static final byte[] POWER_ON = new byte[]{HdmiControlManager.POWER_STATUS_ON};
+ private static final byte[] POWER_TRANSIENT_TO_ON =
+ new byte[]{HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON};
+
+ private static final int PORT_1 = 1;
+ private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
+ ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
+ 0x1234, "TV",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ private Context mContextSpy;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPhysicalAddress;
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mHdmiControlService = new HdmiControlService(mContextSpy) {
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public void setWiredDeviceConnectionState(
+ int type, int state, String address, String name) {
+ // Do nothing.
+ }
+ };
+ }
+
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ protected PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+ };
+
+ Looper looper = mTestLooper.getLooper();
+ mHdmiControlService.setIoLooper(looper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+ mNativeWrapper = new FakeNativeWrapper();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(hdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiControlService.initService();
+ mPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+ mTestLooper.dispatchAll();
+ }
+
+ private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device,
+ TestActionTimer actionTimer, TestCallback callback, boolean isCec20) {
+ OneTouchPlayAction action = new OneTouchPlayAction(device, ADDR_TV, callback, isCec20);
+ action.setActionTimer(actionTimer);
+ return action;
+ }
+
+ @Test
+ public void succeedWithUnknownTvDevice() {
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedAfterGettingPowerStatusOn_Cec14b() {
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedAfterGettingTransientPowerStatus_Cec14b() {
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusTransientToOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_TRANSIENT_TO_ON);
+ action.processCommand(reportPowerStatusTransientToOn);
+ action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void timeOut_Cec14b() {
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ false);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ for (int i = 0; i < 10; ++i) {
+ action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ mTestLooper.dispatchAll();
+ }
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TIMEOUT);
+ }
+
+ @Test
+ public void succeedIfPowerStatusOn_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV,
+ HdmiControlManager.POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ true);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedIfPowerStatusUnknown_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV,
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ true);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void succeedIfPowerStatusStandby_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+ HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+ mHdmiControlService);
+ playbackDevice.init();
+ mLocalDevices.add(playbackDevice);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV,
+ HdmiControlManager.POWER_STATUS_STANDBY);
+ mTestLooper.dispatchAll();
+
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+ true);
+ playbackDevice.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.mAddress, mPhysicalAddress);
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+ ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+ .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+ HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+ ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ action.processCommand(reportPowerStatusOn);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ private static class TestActionTimer implements ActionTimer {
+ private int mState;
+
+ @Override
+ public void sendTimerMessage(int state, long delayMillis) {
+ mState = state;
+ }
+
+ @Override
+ public void clearTimerMessage() {
+ }
+
+ private int getState() {
+ return mState;
+ }
+ }
+
+ private static class TestCallback extends IHdmiControlCallback.Stub {
+ private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assertThat(mCallbackResult.size()).isEqualTo(1);
+ return mCallbackResult.get(0);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 605f781b23df..53b4b491410d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -19,7 +19,6 @@ package com.android.server.hdmi;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
-import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -51,6 +50,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.concurrent.TimeUnit;
/** Tests for {@link ActiveSourceAction} */
@@ -84,7 +84,8 @@ public class PowerStatusMonitorActionTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy,
+ Collections.singletonList(HdmiDeviceInfo.DEVICE_TV)) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -140,6 +141,7 @@ public class PowerStatusMonitorActionTest {
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
}
@Test
@@ -152,7 +154,7 @@ public class PowerStatusMonitorActionTest {
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
@@ -191,7 +193,7 @@ public class PowerStatusMonitorActionTest {
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
@@ -220,12 +222,12 @@ public class PowerStatusMonitorActionTest {
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_2);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2);
}
@@ -245,13 +247,13 @@ public class PowerStatusMonitorActionTest {
mTestLooper.dispatchAll();
HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- ADDR_TV,
+ mTvDevice.mAddress,
ADDR_PLAYBACK_2);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2);
@@ -265,7 +267,7 @@ public class PowerStatusMonitorActionTest {
}
private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) {
- int destination = broadcast ? ADDR_BROADCAST : ADDR_TV;
+ int destination = broadcast ? ADDR_BROADCAST : mTvDevice.mAddress;
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
logicalAddress, destination,
powerStatus);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
new file mode 100644
index 000000000000..865eb7a3b56d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.SystemAudioAutoInitiationAction.RETRIES_ON_TIMEOUT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/**
+ * Test for {@link SystemAudioAutoInitiationAction}.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class SystemAudioAutoInitiationActionTest {
+
+ private Context mContextSpy;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
+ private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPhysicalAddress;
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ Looper myLooper = mTestLooper.getLooper();
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mHdmiControlService = new HdmiControlService(mContextSpy) {
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public void setWiredDeviceConnectionState(
+ int type, int state, String address, String name) {
+ // Do nothing.
+ }
+ };
+ }
+
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ protected PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+ };
+
+ mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mHdmiCecLocalDeviceTv.init();
+ mHdmiControlService.setIoLooper(myLooper);
+ mNativeWrapper = new FakeNativeWrapper();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(hdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDeviceTv);
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+ hdmiPortInfos[1] =
+ new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mHdmiControlService.initService();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+ mTestLooper.dispatchAll();
+ mPhysicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
+ mNativeWrapper.clearResultMessages();
+ }
+
+ private void setSystemAudioSetting(boolean on) {
+ mHdmiCecLocalDeviceTv.setSystemAudioControlFeatureEnabled(on);
+ }
+
+ private void setTvHasSystemAudioChangeAction() {
+ mHdmiCecLocalDeviceTv.addAndStartAction(
+ new SystemAudioActionFromTv(mHdmiCecLocalDeviceTv, Constants.ADDR_AUDIO_SYSTEM,
+ true, null));
+ }
+
+ @Test
+ public void testReceiveSystemAudioMode_systemAudioOn() {
+ // Record that previous system audio mode is on.
+ setSystemAudioSetting(true);
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+ HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.isSystemAudioActivated()).isTrue();
+ }
+
+ @Test
+ public void testReceiveSystemAudioMode_systemAudioOnAndImpossibleToChangeSystemAudio() {
+ // Turn on system audio.
+ setSystemAudioSetting(true);
+ // Impossible to change system audio mode while SystemAudioActionFromTv is in progress.
+ setTvHasSystemAudioChangeAction();
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+ HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.isSystemAudioActivated()).isFalse();
+ }
+
+ @Test
+ public void testReceiveSystemAudioMode_systemAudioOnAndResponseOff() {
+ // Record that previous system audio mode is on.
+ setSystemAudioSetting(true);
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+ HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
+ mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
+ SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
+ SystemAudioActionFromTv.class).get(0);
+ assertThat(resultingAction.mTargetAudioStatus).isTrue();
+ }
+
+ @Test
+ public void testReceiveSystemAudioMode_settingOffAndResponseOn() {
+ // Turn off system audio.
+ setSystemAudioSetting(false);
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+ HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
+ SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
+ SystemAudioActionFromTv.class).get(0);
+ assertThat(resultingAction.mTargetAudioStatus).isFalse();
+ }
+
+ @Test
+ public void testReceiveSystemAudioMode_settingOffAndResponseOff() {
+ // Turn off system audio.
+ setSystemAudioSetting(false);
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+ HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
+ mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isEmpty();
+ assertThat(mHdmiControlService.isSystemAudioActivated()).isFalse();
+ }
+
+ @Test
+ public void testTimeout_systemAudioOn_retries() {
+ // Turn on system audio.
+ setSystemAudioSetting(true);
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+ mNativeWrapper.clearResultMessages();
+
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Retry sends <Give System Audio Mode Status> again
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+ }
+
+ @Test
+ public void testTimeout_systemAudioOn_allRetriesFail() {
+ boolean targetStatus = true;
+ // Turn on system audio.
+ setSystemAudioSetting(targetStatus);
+
+ HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+ ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.addAndStartAction(action);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveSystemAudioModeStatus =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+ for (int i = 0; i < RETRIES_ON_TIMEOUT; i++) {
+ mNativeWrapper.clearResultMessages();
+
+ // Target device doesn't respond within timeout
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Retry sends <Give System Audio Mode Status> again
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+ }
+
+ // Target device doesn't respond within timeouts
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
+ SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
+ SystemAudioActionFromTv.class).get(0);
+ assertThat(resultingAction.mTargetAudioStatus).isEqualTo(targetStatus);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
index 1db5544871bf..d07831dd7929 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -11,15 +11,15 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.server.inputmethod;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 6ab48e53648c..9092ec325946 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -177,10 +177,10 @@ public class InputMethodSubtypeSwitchingControllerTest {
private void assertRotationOrder(final ControllerImpl controller,
final boolean onlyCurrentIme,
final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
- final int N = expectedRotationOrderOfImeSubtypeList.length;
- for (int i = 0; i < N; i++) {
+ final int numItems = expectedRotationOrderOfImeSubtypeList.length;
+ for (int i = 0; i < numItems; i++) {
final int currentIndex = i;
- final int nextIndex = (currentIndex + 1) % N;
+ final int nextIndex = (currentIndex + 1) % numItems;
final ImeSubtypeListItem currentItem =
expectedRotationOrderOfImeSubtypeList[currentIndex];
final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
@@ -200,47 +200,47 @@ public class InputMethodSubtypeSwitchingControllerTest {
@Test
public void testControllerImpl() throws Exception {
final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
- final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
+ final ImeSubtypeListItem disabledIme_en_us = disabledItems.get(0);
final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);
final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
- final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+ final ImeSubtypeListItem latinIme_en_us = enabledItems.get(0);
final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
- final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
- final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
+ final ImeSubtypeListItem switchingUnawareLatinIme_en_uk = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawareLatinIme_hi = enabledItems.get(3);
final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
- final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
- final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
+ final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(5);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(6);
final ControllerImpl controller = ControllerImpl.createFrom(
null /* currentInstance */, enabledItems);
// switching-aware loop
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
+ latinIme_en_us, latinIme_fr, japaneseIme_ja_jp);
// switching-unaware loop
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
- switchUnawareJapaneseIme_ja_JP);
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_jp);
// test onlyCurrentIme == true
assertRotationOrder(controller, true /* onlyCurrentIme */,
- latinIme_en_US, latinIme_fr);
+ latinIme_en_us, latinIme_fr);
assertRotationOrder(controller, true /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
subtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
- japaneseIme_ja_JP, null);
+ japaneseIme_ja_jp, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
- switchUnawareJapaneseIme_ja_JP, null);
+ switchUnawareJapaneseIme_ja_jp, null);
// Make sure that disabled IMEs are not accepted.
assertNextInputMethod(controller, false /* onlyCurrentIme */,
- disabledIme_en_US, null);
+ disabledIme_en_us, null);
assertNextInputMethod(controller, false /* onlyCurrentIme */,
disabledIme_hi, null);
assertNextInputMethod(controller, false /* onlyCurrentIme */,
@@ -248,7 +248,7 @@ public class InputMethodSubtypeSwitchingControllerTest {
assertNextInputMethod(controller, false /* onlyCurrentIme */,
disabledSubtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
- disabledIme_en_US, null);
+ disabledIme_en_us, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
disabledIme_hi, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
@@ -260,82 +260,82 @@ public class InputMethodSubtypeSwitchingControllerTest {
@Test
public void testControllerImplWithUserAction() throws Exception {
final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
- final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+ final ImeSubtypeListItem latinIme_en_us = enabledItems.get(0);
final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
- final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawarelatinIme_en_uk = enabledItems.get(2);
final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
- final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
- final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
+ final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(5);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(6);
final ControllerImpl controller = ControllerImpl.createFrom(
null /* currentInstance */, enabledItems);
// === switching-aware loop ===
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
+ latinIme_en_us, latinIme_fr, japaneseIme_ja_jp);
// Then notify that a user did something for latinIme_fr.
onUserAction(controller, latinIme_fr);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
+ latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
// Then notify that a user did something for latinIme_fr again.
onUserAction(controller, latinIme_fr);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
+ latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
// Then notify that a user did something for japaneseIme_ja_JP.
onUserAction(controller, latinIme_fr);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
+ japaneseIme_ja_jp, latinIme_fr, latinIme_en_us);
// Check onlyCurrentIme == true.
assertNextInputMethod(controller, true /* onlyCurrentIme */,
- japaneseIme_ja_JP, null);
+ japaneseIme_ja_jp, null);
assertRotationOrder(controller, true /* onlyCurrentIme */,
- latinIme_fr, latinIme_en_US);
+ latinIme_fr, latinIme_en_us);
assertRotationOrder(controller, true /* onlyCurrentIme */,
- latinIme_en_US, latinIme_fr);
+ latinIme_en_us, latinIme_fr);
// === switching-unaware loop ===
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
- switchUnawareJapaneseIme_ja_JP);
+ switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_jp);
// User action should be ignored for switching unaware IMEs.
onUserAction(controller, switchingUnawarelatinIme_hi);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
- switchUnawareJapaneseIme_ja_JP);
+ switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_jp);
// User action should be ignored for switching unaware IMEs.
- onUserAction(controller, switchUnawareJapaneseIme_ja_JP);
+ onUserAction(controller, switchUnawareJapaneseIme_ja_jp);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
- switchUnawareJapaneseIme_ja_JP);
+ switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_jp);
// Check onlyCurrentIme == true.
assertRotationOrder(controller, true /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
subtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
- switchUnawareJapaneseIme_ja_JP, null);
+ switchUnawareJapaneseIme_ja_jp, null);
// Rotation order should be preserved when created with the same subtype list.
final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
final ControllerImpl newController = ControllerImpl.createFrom(controller,
sameEnabledItems);
assertRotationOrder(newController, false /* onlyCurrentIme */,
- japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
+ japaneseIme_ja_jp, latinIme_fr, latinIme_en_us);
assertRotationOrder(newController, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
- switchUnawareJapaneseIme_ja_JP);
+ switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_jp);
// Rotation order should be initialized when created with a different subtype list.
final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList(
- latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK,
- switchUnawareJapaneseIme_ja_JP);
+ latinIme_en_us, latinIme_fr, switchingUnawarelatinIme_en_uk,
+ switchUnawareJapaneseIme_ja_jp);
final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
differentEnabledItems);
assertRotationOrder(anotherController, false /* onlyCurrentIme */,
- latinIme_en_US, latinIme_fr);
+ latinIme_en_us, latinIme_fr);
assertRotationOrder(anotherController, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP);
+ switchingUnawarelatinIme_en_uk, switchUnawareJapaneseIme_ja_jp);
}
@Test
@@ -344,27 +344,27 @@ public class InputMethodSubtypeSwitchingControllerTest {
addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme",
Arrays.asList("en_US", "fr", "en", "en_uk", "enn", "e", "EN_US"),
true /* supportsSwitchingToNextInputMethod*/);
- final ImeSubtypeListItem item_en_US = items.get(0);
+ final ImeSubtypeListItem item_en_us = items.get(0);
final ImeSubtypeListItem item_fr = items.get(1);
final ImeSubtypeListItem item_en = items.get(2);
final ImeSubtypeListItem item_enn = items.get(3);
final ImeSubtypeListItem item_e = items.get(4);
- final ImeSubtypeListItem item_EN_US = items.get(5);
+ final ImeSubtypeListItem item_en_us_allcaps = items.get(5);
- assertTrue(item_en_US.mIsSystemLocale);
+ assertTrue(item_en_us.mIsSystemLocale);
assertFalse(item_fr.mIsSystemLocale);
assertFalse(item_en.mIsSystemLocale);
assertFalse(item_en.mIsSystemLocale);
assertFalse(item_enn.mIsSystemLocale);
assertFalse(item_e.mIsSystemLocale);
- assertFalse(item_EN_US.mIsSystemLocale);
+ assertFalse(item_en_us_allcaps.mIsSystemLocale);
- assertTrue(item_en_US.mIsSystemLanguage);
+ assertTrue(item_en_us.mIsSystemLanguage);
assertFalse(item_fr.mIsSystemLanguage);
assertTrue(item_en.mIsSystemLanguage);
assertFalse(item_enn.mIsSystemLocale);
assertFalse(item_e.mIsSystemLocale);
- assertFalse(item_EN_US.mIsSystemLocale);
+ assertFalse(item_en_us_allcaps.mIsSystemLocale);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 1d914ec083fa..eebc25aab279 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -60,6 +60,7 @@ public class InputMethodUtilsTest {
private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true;
private static final boolean IS_ASCII_CAPABLE = true;
private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true;
+ private static final boolean CHECK_COUNTRY = true;
private static final Locale LOCALE_EN = new Locale("en");
private static final Locale LOCALE_EN_US = new Locale("en", "US");
private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
@@ -668,8 +669,6 @@ public class InputMethodUtilsTest {
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
- final boolean CHECK_COUNTRY = true;
-
{
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
subtypes.add(nonAutoEnUS);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b51f4df43259..91342ce925f6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -109,7 +110,8 @@ public class RebootEscrowManagerTests {
public interface MockableRebootEscrowInjected {
int getBootCount();
- void reportMetric(boolean success);
+ void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+ int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
}
static class MockInjector extends RebootEscrowManager.Injector {
@@ -119,6 +121,7 @@ public class RebootEscrowManagerTests {
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
+ private final boolean mServerBased;
MockInjector(Context context, UserManager userManager,
IRebootEscrow rebootEscrow,
@@ -128,6 +131,7 @@ public class RebootEscrowManagerTests {
super(context, storage);
mRebootEscrow = rebootEscrow;
mServiceConnection = null;
+ mServerBased = false;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@Override
@@ -149,6 +153,7 @@ public class RebootEscrowManagerTests {
super(context, storage);
mServiceConnection = serviceConnection;
mRebootEscrow = null;
+ mServerBased = true;
RebootEscrowProviderServerBasedImpl.Injector injector =
new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
@@ -168,6 +173,11 @@ public class RebootEscrowManagerTests {
}
@Override
+ public boolean serverBasedResumeOnReboot() {
+ return mServerBased;
+ }
+
+ @Override
public RebootEscrowProviderInterface getRebootEscrowProvider() {
return mRebootEscrowProvider;
}
@@ -195,8 +205,11 @@ public class RebootEscrowManagerTests {
}
@Override
- public void reportMetric(boolean success) {
- mInjected.reportMetric(success);
+ public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+ int escrowDurationInSeconds, int vbmetaDigestStatus,
+ int durationSinceBootComplete) {
+ mInjected.reportMetric(success, errorCode, serviceType, attemptCount,
+ escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete);
}
}
@@ -418,7 +431,9 @@ public class RebootEscrowManagerTests {
when(mInjected.getBootCount()).thenReturn(1);
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
- doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+ eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */,
+ anyInt(), anyInt(), anyInt());
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
mService.loadRebootEscrowDataIfAvailable(null);
@@ -451,7 +466,9 @@ public class RebootEscrowManagerTests {
// pretend reboot happens here
when(mInjected.getBootCount()).thenReturn(1);
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
- doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+ eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */,
+ anyInt(), anyInt(), anyInt());
when(mServiceConnection.unwrap(any(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
@@ -485,7 +502,8 @@ public class RebootEscrowManagerTests {
// pretend reboot happens here
when(mInjected.getBootCount()).thenReturn(1);
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
- doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+ anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt());
when(mServiceConnection.unwrap(any(), anyLong()))
.thenThrow(new IOException())
@@ -528,7 +546,8 @@ public class RebootEscrowManagerTests {
mService.loadRebootEscrowDataIfAvailable(null);
verify(mRebootEscrow).retrieveKey();
- verify(mInjected, never()).reportMetric(anyBoolean());
+ verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
+ anyInt(), anyInt(), anyInt());
}
@Test
@@ -554,7 +573,8 @@ public class RebootEscrowManagerTests {
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
mService.loadRebootEscrowDataIfAvailable(null);
- verify(mInjected, never()).reportMetric(anyBoolean());
+ verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
+ anyInt(), anyInt(), anyInt());
}
@Test
@@ -588,7 +608,8 @@ public class RebootEscrowManagerTests {
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
mService.loadRebootEscrowDataIfAvailable(null);
- verify(mInjected).reportMetric(eq(true));
+ verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */,
+ eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());
}
@Test
@@ -615,7 +636,9 @@ public class RebootEscrowManagerTests {
when(mInjected.getBootCount()).thenReturn(1);
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
- doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+ anyInt() /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */,
+ anyInt(), anyInt(), anyInt());
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
mService.loadRebootEscrowDataIfAvailable(null);
verify(mRebootEscrow).retrieveKey();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index a38745f2a66e..d9af51f819c3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -42,7 +42,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.FileUtils;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.recovery.KeyChainSnapshot;
@@ -109,7 +108,7 @@ public class KeySyncTaskTest {
private RecoverySnapshotStorage mRecoverySnapshotStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
- private AndroidKeyStoreSecretKey mWrappingKey;
+ private SecretKey mWrappingKey;
private PlatformEncryptionKey mEncryptKey;
private KeySyncTask mKeySyncTask;
@@ -848,7 +847,7 @@ public class KeySyncTaskTest {
return keyGenerator.generateKey();
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -857,7 +856,7 @@ public class KeySyncTaskTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
private static byte[] utf8Bytes(String s) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index dd3054f6543c..3fd2c97075ac 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -32,7 +32,6 @@ import android.app.KeyguardManager;
import android.content.Context;
import android.os.RemoteException;
import android.security.GateKeeper;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
@@ -59,6 +58,7 @@ import java.security.UnrecoverableKeyException;
import java.util.List;
import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -575,7 +575,7 @@ public class PlatformKeyManagerTest {
return (KeyProtection) mProtectionParameterCaptor.getValue();
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -584,7 +584,7 @@ public class PlatformKeyManagerTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
class PlatformKeyManagerTestable extends PlatformKeyManager {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index c295177814b0..64130266b2c4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import android.content.Context;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -45,6 +44,7 @@ import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
@SmallTest
@@ -77,7 +77,7 @@ public class RecoverableKeyGeneratorTest {
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
- AndroidKeyStoreSecretKey platformKey = generatePlatformKey();
+ SecretKey platformKey = generatePlatformKey();
mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
@@ -168,7 +168,7 @@ public class RecoverableKeyGeneratorTest {
assertArrayEquals(rawMaterial, unwrappedMaterial);
}
- private AndroidKeyStoreSecretKey generatePlatformKey() throws Exception {
+ private SecretKey generatePlatformKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -177,7 +177,7 @@ public class RecoverableKeyGeneratorTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
private static byte[] randomBytes(int n) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b65e4877da50..a227cd3c6f5c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -45,7 +45,6 @@ import android.content.Intent;
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -1311,7 +1310,7 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -1320,6 +1319,6 @@ public class RecoverableKeyStoreManagerTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index 9813ab74721e..60052f7114b3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Pair;
@@ -117,7 +116,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception {
String alias = "karlin";
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
SecretKey appKey = generateKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -136,7 +135,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception {
String alias = "karlin";
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
SecretKey appKey = generateKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA);
@@ -155,7 +154,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
String alias = "karlin";
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
SecretKey appKey = generateKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -171,7 +170,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(),
/*metadata=*/ null);
@@ -197,7 +196,7 @@ public class WrappedKeyTest {
return keyGenerator.generateKey();
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -207,6 +206,6 @@ public class WrappedKeyTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9a52643b57f2..9f428c7cbded 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.parsing.ParsingPackage;
@@ -141,6 +142,10 @@ public class AppsFilterTest {
return pkg(packageName).addReceiver(receiver);
}
+ private static ParsingPackage pkgWithSharedLibrary(String packageName, String libName) {
+ return pkg(packageName).addLibraryName(libName);
+ }
+
private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) {
ParsedActivity activity = new ParsedActivity();
activity.setPackageName(packageName);
@@ -413,6 +418,118 @@ public class AppsFilterTest {
}
@Test
+ public void testNoUsesLibrary_Filters() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
+ public void testUsesLibrary_DoesntFilter() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package").addUsesLibrary("com.some.shared_library"),
+ DUMMY_CALLING_APPID);
+
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
+ public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package").addUsesOptionalLibrary("com.some.shared_library"),
+ DUMMY_CALLING_APPID);
+
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
+ public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package_a").setSharedUserId("com.some.uid"),
+ DUMMY_CALLING_APPID);
+ simulateAddPackage(appsFilter, pkg("com.some.other.package_b")
+ .setSharedUserId("com.some.uid").addUsesLibrary("com.some.shared_library"),
+ DUMMY_CALLING_APPID);
+
+ // Although package_a doesn't use library, it should be granted visibility. It's because
+ // package_a shares userId with package_b, and package_b uses that shared library.
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
public void testForceQueryable_SystemDoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
new file mode 100644
index 000000000000..e605d755183f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static org.mockito.Mockito.inOrder;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Unit tests for {@link com.android.server.pm.dex.ArtStatsLogUtils}.
+ *
+ * Run with "atest ArtStatsLogUtilsTest".
+ */
+@RunWith(JUnit4.class)
+public final class ArtStatsLogUtilsTest {
+ private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName();
+ private static final String COMPILER_FILTER = "space-profile";
+ private static final String PROFILE_DEX_METADATA = "primary.prof";
+ private static final String VDEX_DEX_METADATA = "primary.vdex";
+ private static final byte[] DEX_CONTENT = "dexData".getBytes();
+ private static final int COMPILATION_REASON = 1;
+ private static final int RESULT_CODE = 222;
+ private static final int UID = 111;
+ private static final long COMPILE_TIME = 333L;
+ private static final long SESSION_ID = 444L;
+
+ @Mock
+ ArtStatsLogger mockLogger;
+
+ private static Path TEST_DIR;
+ private static Path DEX;
+ private static Path NON_DEX;
+
+ @BeforeClass
+ public static void setUpAll() throws IOException {
+ TEST_DIR = Files.createTempDirectory(null);
+ DEX = Files.createFile(TEST_DIR.resolve("classes.dex"));
+ NON_DEX = Files.createFile(TEST_DIR.resolve("test.dex"));
+ Files.write(DEX, DEX_CONTENT);
+ Files.write(NON_DEX, "empty".getBytes());
+ }
+
+ @AfterClass
+ public static void tearnDownAll() {
+ deleteSliently(DEX);
+ deleteSliently(NON_DEX);
+ deleteSliently(TEST_DIR);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testProfileAndVdexDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA, VDEX_DEX_METADATA);
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testProfileOnlyDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA);
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testVdexOnlyDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata(VDEX_DEX_METADATA);
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testNoneDexMetadata() throws IOException {
+ // Setup
+ Path apk = null;
+ try {
+ apk = zipFiles(".apk", DEX, NON_DEX);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ /*dexMetadataPath=*/ null,
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE);
+ } finally {
+ deleteSliently(apk);
+ }
+ }
+
+ @Test
+ public void testUnKnownDexMetadata() throws IOException {
+ // Setup
+ Path dexMetadataPath = null;
+ Path apk = null;
+ try {
+ dexMetadataPath = createDexMetadata("unknown");
+ apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+ // Act
+ ArtStatsLogUtils.writeStatsLog(
+ mockLogger,
+ SESSION_ID,
+ apk.toString(),
+ COMPILER_FILTER,
+ UID,
+ COMPILE_TIME,
+ dexMetadataPath.toString(),
+ COMPILATION_REASON,
+ RESULT_CODE);
+
+ // Assert
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN);
+ } finally {
+ deleteSliently(dexMetadataPath);
+ deleteSliently(apk);
+ }
+ }
+
+ private void verifyWrites(int dexMetadataType) {
+ InOrder inorder = inOrder(mockLogger);
+ inorder.verify(mockLogger).write(
+ SESSION_ID, UID,
+ COMPILATION_REASON,
+ COMPILER_FILTER,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+ RESULT_CODE,
+ dexMetadataType);
+ inorder.verify(mockLogger).write(
+ SESSION_ID,
+ UID,
+ COMPILATION_REASON,
+ COMPILER_FILTER,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+ DEX_CONTENT.length,
+ dexMetadataType);
+ inorder.verify(mockLogger).write(
+ SESSION_ID,
+ UID,
+ COMPILATION_REASON,
+ COMPILER_FILTER,
+ ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+ COMPILE_TIME,
+ dexMetadataType);
+ }
+
+ private Path zipFiles(String suffix, Path... files) throws IOException {
+ Path zipFile = Files.createTempFile(null, suffix);
+ try (final OutputStream os = Files.newOutputStream(zipFile)) {
+ try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+ for (Path file : files) {
+ ZipEntry zipEntry = new ZipEntry(file.getFileName().toString());
+ zos.putNextEntry(zipEntry);
+ zos.write(Files.readAllBytes(file));
+ zos.closeEntry();
+ }
+ }
+ }
+ return zipFile;
+ }
+
+ private Path createDexMetadata(String... entryNames) throws IOException {
+ Path zipFile = Files.createTempFile(null, ".dm");
+ try (final OutputStream os = Files.newOutputStream(zipFile)) {
+ try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+ for (String entryName : entryNames) {
+ ZipEntry zipEntry = new ZipEntry(entryName);
+ zos.putNextEntry(zipEntry);
+ zos.write(entryName.getBytes());
+ zos.closeEntry();
+ }
+ }
+ }
+ return zipFile;
+ }
+
+ private static void deleteSliently(Path file) {
+ if (file != null) {
+ try {
+ Files.deleteIfExists(file);
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
index 22c38c1961b8..fa2123c0d09a 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -31,6 +31,7 @@ import android.content.pm.ServiceInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.rotationresolver.RotationResolverInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
import android.view.Surface;
import androidx.test.core.app.ApplicationProvider;
@@ -58,6 +59,7 @@ public class RotationResolverManagerPerUserServiceTest {
private Context mContext;
private CancellationSignal mCancellationSignal;
private RotationResolverManagerPerUserService mService;
+ private RotationResolutionRequest mRequest;
@Before
public void setUp() throws RemoteException {
@@ -79,9 +81,10 @@ public class RotationResolverManagerPerUserServiceTest {
mCancellationSignal = new CancellationSignal();
+ mRequest = new RotationResolutionRequest("", Surface.ROTATION_0, Surface.ROTATION_0,
+ true, 1000L);
this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
- mMockCallbackInternal, Surface.ROTATION_0, Surface.ROTATION_0, "", 1000L,
- mCancellationSignal);
+ mMockCallbackInternal, mRequest, mCancellationSignal);
this.mService.getMaster().mIsServiceEnabled = true;
@@ -99,8 +102,7 @@ public class RotationResolverManagerPerUserServiceTest {
RotationResolverInternal.RotationResolverCallbackInternal callbackInternal =
Mockito.mock(RotationResolverInternal.RotationResolverCallbackInternal.class);
- mService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
- "", 1000L, mCancellationSignal);
+ mService.resolveRotationLocked(callbackInternal, mRequest, mCancellationSignal);
verify(callbackInternal).onSuccess(anyInt());
}
@@ -110,8 +112,7 @@ public class RotationResolverManagerPerUserServiceTest {
Mockito.mock(RotationResolverInternal.RotationResolverCallbackInternal.class);
final CancellationSignal cancellationSignal = new CancellationSignal();
- mService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
- "", 1000L, cancellationSignal);
+ mService.resolveRotationLocked(callbackInternal, mRequest, cancellationSignal);
cancellationSignal.cancel();
}
@@ -132,7 +133,7 @@ public class RotationResolverManagerPerUserServiceTest {
@Override
public void resolveRotationLocked(RotationRequest request) {
- request.mCallbackInternal.onSuccess(request.mProposedRotation);
+ request.mCallbackInternal.onSuccess(request.mRemoteRequest.getProposedRotation());
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
new file mode 100644
index 000000000000..d4222e6e0b63
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timedetector;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.time.Capabilities;
+import android.app.time.TimeCapabilities;
+import android.app.time.TimeCapabilitiesAndConfig;
+import android.app.time.TimeConfiguration;
+import android.os.UserHandle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ConfigurationInternalTest {
+
+ @Test
+ public void capabilitiesAndConfig() {
+ int userId = 112233;
+ ConfigurationInternal configurationInternal = new ConfigurationInternal.Builder(userId)
+ .setAutoDetectionEnabled(true)
+ .setUserConfigAllowed(true)
+ .build();
+
+ TimeCapabilities timeCapabilities = new TimeCapabilities.Builder(UserHandle.of(userId))
+ .setConfigureAutoTimeDetectionEnabledCapability(Capabilities.CAPABILITY_POSSESSED)
+ .setSuggestTimeManuallyCapability(Capabilities.CAPABILITY_POSSESSED)
+ .build();
+ TimeConfiguration timeConfiguration = new TimeConfiguration.Builder()
+ .setAutoDetectionEnabled(true)
+ .build();
+ TimeCapabilitiesAndConfig expected =
+ new TimeCapabilitiesAndConfig(timeCapabilities, timeConfiguration);
+
+ assertThat(configurationInternal.capabilitiesAndConfig()).isEqualTo(expected);
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 5f86d282406a..106827078290 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -209,7 +209,7 @@ public class TimeDetectorServiceTest {
@Test(expected = SecurityException.class)
public void testSuggestExternalTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
ExternalTimeSuggestion externalTimeSuggestion = createExternalTimeSuggestion();
try {
@@ -328,6 +328,11 @@ public class TimeDetectorServiceTest {
}
@Override
+ public ConfigurationInternal getConfigurationInternal(int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void handleAutoTimeConfigChanged() {
mHandleAutoTimeDetectionChangedCalled = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index f7a498bc9d73..095703e6b939 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -1158,6 +1158,11 @@ public class TimeDetectorStrategyImplTest {
}
@Override
+ public ConfigurationInternal configurationInternal(int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void acquireWakeLock() {
if (mWakeLockAcquired) {
fail("Wake lock already acquired");
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 5d2755221288..aa46e7ef658f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -16,10 +16,10 @@
package com.android.server.timezonedetector;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -46,7 +46,7 @@ public class ConfigurationInternalTest {
public void test_unrestricted() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -108,7 +108,7 @@ public class ConfigurationInternalTest {
public void test_restricted() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(false)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -170,7 +170,7 @@ public class ConfigurationInternalTest {
public void test_autoDetectNotSupported() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(false)
+ .setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -232,7 +232,7 @@ public class ConfigurationInternalTest {
public void test_geoDetectNotSupported() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index bad380acf4b3..51f627ab415c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -118,6 +118,11 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
}
@Override
+ public MetricsTimeZoneDetectorState generateMetricsState() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void addDumpable(Dumpable dumpable) {
mDumpables.add(dumpable);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
new file mode 100644
index 000000000000..af954d599334
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class OrdinalGeneratorTest {
+
+ @Test
+ public void testOrdinal() {
+ OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+ int oneOrd = ordinalGenerator.ordinal("One");
+ int twoOrd = ordinalGenerator.ordinal("Two");
+ assertNotEquals(oneOrd, twoOrd);
+
+ assertEquals(oneOrd, ordinalGenerator.ordinal("One"));
+ assertEquals(twoOrd, ordinalGenerator.ordinal("Two"));
+
+ int threeOrd = ordinalGenerator.ordinal("Three");
+ assertNotEquals(oneOrd, threeOrd);
+ assertNotEquals(twoOrd, threeOrd);
+ }
+
+ @Test
+ public void testOrdinals() {
+ OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+ int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two"));
+ int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three"));
+ assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("One"));
+ assertEquals(oneTwoOrds[1], ordinalGenerator.ordinal("Two"));
+ assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("Two"));
+ assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("Three"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 14e0bbd6fa42..8af2c4d04fd9 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -364,7 +364,7 @@ public class TimeZoneDetectorServiceTest {
// the tests.
final boolean geoDetectionEnabled = autoDetectionEnabled;
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(autoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index f1f8b2f5e81a..f91ce87e8f08 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -90,7 +90,7 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
@@ -100,7 +100,7 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -110,17 +110,17 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(false)
+ .setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
.build();
- private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
+ private static final ConfigurationInternal CONFIG_INT_TELEPHONY_SUPPORTED_GEO_NOT_SUPPORTED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -130,7 +130,7 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
@@ -139,7 +139,7 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(true)
@@ -149,7 +149,7 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(true)
@@ -266,7 +266,8 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testUpdateConfiguration_autoDetectSupportedGeoNotSupported() {
- Script script = new Script().initializeConfig(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED);
+ Script script = new Script().initializeConfig(
+ CONFIG_INT_TELEPHONY_SUPPORTED_GEO_NOT_SUPPORTED);
// Update the configuration with auto detection disabled.
script.simulateUpdateConfiguration(
@@ -274,7 +275,7 @@ public class TimeZoneDetectorStrategyImplTest {
// The settings should have been changed and the StrategyListener onChange() called.
ConfigurationInternal expectedConfig =
- new ConfigurationInternal.Builder(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED)
+ new ConfigurationInternal.Builder(CONFIG_INT_TELEPHONY_SUPPORTED_GEO_NOT_SUPPORTED)
.setAutoDetectionEnabled(false)
.build();
script.verifyConfigurationChangedAndReset(expectedConfig);
@@ -675,6 +676,8 @@ public class TimeZoneDetectorStrategyImplTest {
script.simulateManualTimeZoneSuggestion(
USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
.verifyTimeZoneNotChanged();
+
+ assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
@@ -687,6 +690,8 @@ public class TimeZoneDetectorStrategyImplTest {
script.simulateManualTimeZoneSuggestion(
USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
.verifyTimeZoneNotChanged();
+
+ assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
@@ -700,6 +705,8 @@ public class TimeZoneDetectorStrategyImplTest {
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(manualSuggestion);
+
+ assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
@@ -713,6 +720,8 @@ public class TimeZoneDetectorStrategyImplTest {
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, false /* expectedResult */)
.verifyTimeZoneNotChanged();
+
+ assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
@@ -726,6 +735,8 @@ public class TimeZoneDetectorStrategyImplTest {
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(manualSuggestion);
+
+ assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
}
@Test
@@ -914,6 +925,106 @@ public class TimeZoneDetectorStrategyImplTest {
assertTrue(dumpCalled.get());
}
+ @Test
+ public void testGenerateMetricsState() {
+ ConfigurationInternal expectedInternalConfig = CONFIG_INT_AUTO_DISABLED_GEO_DISABLED;
+ String expectedDeviceTimeZoneId = "InitialZoneId";
+
+ Script script = new Script()
+ .initializeConfig(expectedInternalConfig)
+ .initializeTimeZoneSetting(expectedDeviceTimeZoneId);
+
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, null, null,
+ null, MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+ // Make sure the manual suggestion is recorded.
+ ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1");
+ script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion,
+ true /* expectedResult */)
+ .verifyTimeZoneChangedAndReset(manualSuggestion);
+ expectedDeviceTimeZoneId = manualSuggestion.getZoneId();
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, null, null,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+ // With time zone auto detection off, telephony suggestions will be recorded, but geo
+ // suggestions won't out of an abundance of caution around respecting user privacy when
+ // geo detection is off.
+ TelephonyTimeZoneSuggestion telephonySuggestion =
+ createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ QUALITY_SINGLE_ZONE, "Zone2");
+ GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion =
+ createGeoLocationSuggestion(Arrays.asList("Zone3", "Zone2"));
+ script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+ .verifyTimeZoneNotChanged()
+ .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+ .verifyTimeZoneNotChanged();
+
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+ // Update the config and confirm that the config metrics state updates also.
+ TimeZoneConfiguration configUpdate =
+ createConfig(true /* autoDetection */, true /* geoDetection */);
+ expectedInternalConfig = new ConfigurationInternal.Builder(expectedInternalConfig)
+ .setAutoDetectionEnabled(true)
+ .setGeoDetectionEnabled(true)
+ .build();
+ script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
+ .verifyConfigurationChangedAndReset(expectedInternalConfig)
+ .verifyTimeZoneNotChanged();
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+
+ // Now simulate a geo suggestion and confirm it is used and reported in the metrics too.
+ expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
+ script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+ .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+ }
+
+ /**
+ * Asserts that the information returned by {@link
+ * TimeZoneDetectorStrategy#generateMetricsState()} matches expectations.
+ */
+ private void assertMetricsState(
+ ConfigurationInternal expectedInternalConfig,
+ String expectedDeviceTimeZoneId, ManualTimeZoneSuggestion expectedManualSuggestion,
+ TelephonyTimeZoneSuggestion expectedTelephonySuggestion,
+ GeolocationTimeZoneSuggestion expectedGeolocationTimeZoneSuggestion,
+ int expectedDetectionMode) {
+
+ MetricsTimeZoneDetectorState actualState = mTimeZoneDetectorStrategy.generateMetricsState();
+
+ // Check the various feature state values are what we expect.
+ assertFeatureStateMatchesConfig(expectedInternalConfig, actualState, expectedDetectionMode);
+
+ OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+ MetricsTimeZoneDetectorState expectedState =
+ MetricsTimeZoneDetectorState.create(
+ tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
+ expectedManualSuggestion, expectedTelephonySuggestion,
+ expectedGeolocationTimeZoneSuggestion);
+ // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons.
+ assertEquals(expectedState, actualState);
+ }
+
+ private static void assertFeatureStateMatchesConfig(ConfigurationInternal config,
+ MetricsTimeZoneDetectorState actualState, int expectedDetectionMode) {
+ assertEquals(config.isTelephonyDetectionSupported(),
+ actualState.isTelephonyDetectionSupported());
+ assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported());
+ assertEquals(config.getAutoDetectionEnabledSetting(),
+ actualState.getAutoDetectionEnabledSetting());
+ assertEquals(config.getGeoDetectionEnabledSetting(),
+ actualState.getGeoDetectionEnabledSetting());
+ assertEquals(expectedDetectionMode, actualState.getDetectionMode());
+ }
+
private static ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
return new ManualTimeZoneSuggestion(zoneId);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 4284240c72b4..5a100a297cfc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -72,18 +72,25 @@ public class ControllerImplTest {
private TestCallback mTestCallback;
private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
+ private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;
@Before
public void setUp() {
// For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
// runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
// will never get a chance to execute.
+ LocationTimeZoneProvider.ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> {
+ // Stubbed.
+ };
mTestThreadingDomain = new TestThreadingDomain();
mTestCallback = new TestCallback(mTestThreadingDomain);
- mTestPrimaryLocationTimeZoneProvider =
- new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary");
- mTestSecondaryLocationTimeZoneProvider =
- new TestLocationTimeZoneProvider(mTestThreadingDomain, "secondary");
+ mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
+ mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+ stubbedProviderMetricsLogger, mTestThreadingDomain, "primary",
+ mTimeZoneAvailabilityChecker);
+ mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+ stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary",
+ mTimeZoneAvailabilityChecker);
}
@Test
@@ -1177,8 +1184,11 @@ public class ControllerImplTest {
/**
* Creates the instance.
*/
- TestLocationTimeZoneProvider(ThreadingDomain threadingDomain, String providerName) {
- super(threadingDomain, providerName);
+ TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
+ ThreadingDomain threadingDomain, String providerName,
+ TimeZoneIdValidator timeZoneIdValidator) {
+ super(providerMetricsLogger, threadingDomain, providerName,
+ timeZoneIdValidator);
}
public void setFailDuringInitialization(boolean failInitialization) {
@@ -1311,4 +1321,14 @@ public class ControllerImplTest {
mTestProviderState.commitLatest();
}
}
+
+ private static final class FakeTimeZoneIdValidator
+ implements LocationTimeZoneProvider.TimeZoneIdValidator {
+
+ @Override
+ public boolean isValid(@NonNull String timeZoneId) {
+ return true;
+ }
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index 095c868fc74c..d13a04e13406 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -32,6 +32,8 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static java.util.Arrays.asList;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -50,7 +52,10 @@ import org.junit.Test;
import java.time.Duration;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -62,27 +67,31 @@ public class LocationTimeZoneProviderTest {
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 123456789L;
private TestThreadingDomain mTestThreadingDomain;
-
private TestProviderListener mProviderListener;
+ private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;
@Before
public void setUp() {
mTestThreadingDomain = new TestThreadingDomain();
mProviderListener = new TestProviderListener();
+ mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
}
@Test
public void lifecycle() {
String providerName = "arbitrary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ RecordingProviderMetricsLogger providerMetricsLogger = new RecordingProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
+ mTimeZoneAvailabilityChecker.validIds("Europe/London");
// initialize()
provider.initialize(mProviderListener);
provider.assertOnInitializeCalled();
- ProviderState currentState = provider.getCurrentState();
- assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
assertNull(currentState.currentUserConfiguration);
assertSame(provider, currentState.provider);
mTestThreadingDomain.assertQueueEmpty();
@@ -96,9 +105,9 @@ public class LocationTimeZoneProviderTest {
provider.assertOnStartCalled(arbitraryInitializationTimeout);
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING);
assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
assertEquals(config, currentState.currentUserConfiguration);
assertNull(currentState.event);
// The initialization timeout should be queued.
@@ -120,9 +129,9 @@ public class LocationTimeZoneProviderTest {
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
provider.simulateProviderEventReceived(event);
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN);
assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STARTED_CERTAIN, currentState.stateEnum);
assertEquals(event, currentState.event);
assertEquals(config, currentState.currentUserConfiguration);
mTestThreadingDomain.assertQueueEmpty();
@@ -132,9 +141,9 @@ public class LocationTimeZoneProviderTest {
event = TimeZoneProviderEvent.createUncertainEvent();
provider.simulateProviderEventReceived(event);
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN);
assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, currentState.stateEnum);
assertEquals(event, currentState.event);
assertEquals(config, currentState.currentUserConfiguration);
mTestThreadingDomain.assertQueueEmpty();
@@ -144,7 +153,8 @@ public class LocationTimeZoneProviderTest {
provider.stopUpdates();
provider.assertOnStopUpdatesCalled();
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
assertSame(provider, currentState.provider);
assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
assertNull(currentState.event);
@@ -162,8 +172,10 @@ public class LocationTimeZoneProviderTest {
@Test
public void defaultHandleTestCommandImpl() {
String providerName = "primary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
AtomicReference<Bundle> resultReference = new AtomicReference<>();
@@ -179,9 +191,12 @@ public class LocationTimeZoneProviderTest {
@Test
public void stateRecording() {
String providerName = "primary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
provider.setStateChangeRecordingEnabled(true);
+ mTimeZoneAvailabilityChecker.validIds("Europe/London");
// initialize()
provider.initialize(mProviderListener);
@@ -218,6 +233,33 @@ public class LocationTimeZoneProviderTest {
provider.assertLatestRecordedState(PROVIDER_STATE_DESTROYED);
}
+ @Test
+ public void considerSuggestionWithInvalidTimeZoneIdsAsUncertain() {
+ String providerName = "primary";
+ StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
+ provider.setStateChangeRecordingEnabled(true);
+ provider.initialize(mProviderListener);
+
+ ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
+ Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
+ Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
+ provider.startUpdates(config, arbitraryInitializationTimeout,
+ arbitraryInitializationTimeoutFuzz);
+
+ List<String> invalidTimeZoneIds = asList("Atlantic/Atlantis");
+ TimeZoneProviderSuggestion invalidIdSuggestion = new TimeZoneProviderSuggestion.Builder()
+ .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .setTimeZoneIds(invalidTimeZoneIds)
+ .build();
+ TimeZoneProviderEvent event =
+ TimeZoneProviderEvent.createSuggestionEvent(invalidIdSuggestion);
+ provider.simulateProviderEventReceived(event);
+ provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN);
+ }
+
/** A test stand-in for the real {@link LocationTimeZoneProviderController}'s listener. */
private static class TestProviderListener implements ProviderListener {
@@ -241,6 +283,20 @@ public class LocationTimeZoneProviderTest {
}
}
+ /**
+ * Returns the provider's state after asserting that the current state matches what is expected.
+ * This also asserts that the metrics logger was informed of the state change.
+ */
+ private static ProviderState assertAndReturnProviderState(
+ TestLocationTimeZoneProvider provider,
+ RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum) {
+ ProviderState currentState = provider.getCurrentState();
+ assertEquals(expectedStateEnum, currentState.stateEnum);
+ providerMetricsLogger.assertChangeLoggedAndRemove(expectedStateEnum);
+ providerMetricsLogger.assertNoMoreLogEntries();
+ return currentState;
+ }
+
private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
private boolean mOnInitializeCalled;
@@ -250,9 +306,11 @@ public class LocationTimeZoneProviderTest {
private boolean mOnStopUpdatesCalled;
/** Creates the instance. */
- TestLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
- @NonNull String providerName) {
- super(threadingDomain, providerName);
+ TestLocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+ @NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName,
+ @NonNull TimeZoneIdValidator timeZoneIdValidator) {
+ super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator);
}
@Override
@@ -308,4 +366,49 @@ public class LocationTimeZoneProviderTest {
recordedStates.get(recordedStates.size() - 1).stateEnum);
}
}
+
+ private static final class FakeTimeZoneIdValidator
+ implements LocationTimeZoneProvider.TimeZoneIdValidator {
+ private final Set<String> mValidTimeZoneIds = new HashSet<>();
+
+ @Override
+ public boolean isValid(@NonNull String timeZoneId) {
+ return mValidTimeZoneIds.contains(timeZoneId);
+ }
+
+ public void validIds(String... timeZoneIdss) {
+ mValidTimeZoneIds.addAll(asList(timeZoneIdss));
+ }
+ }
+
+ private static class StubbedProviderMetricsLogger implements
+ LocationTimeZoneProvider.ProviderMetricsLogger {
+
+ @Override
+ public void onProviderStateChanged(int stateEnum) {
+ // Stubbed
+ }
+ }
+
+ private static class RecordingProviderMetricsLogger implements
+ LocationTimeZoneProvider.ProviderMetricsLogger {
+
+ private LinkedList<Integer> mStates = new LinkedList<>();
+
+ @Override
+ public void onProviderStateChanged(int stateEnum) {
+ mStates.add(stateEnum);
+ }
+
+ public void assertChangeLoggedAndRemove(int expectedLoggedState) {
+ assertEquals("expected loggedState=" + expectedLoggedState
+ + " but states logged were=" + mStates,
+ (Integer) expectedLoggedState, mStates.peekFirst());
+ mStates.removeFirst();
+ }
+
+ public void assertNoMoreLogEntries() {
+ assertTrue(mStates.isEmpty());
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index 8280cdcb18c4..16ac1d602de5 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -44,7 +44,7 @@ final class TestSupport {
@UserIdInt int userId, boolean geoDetectionEnabled) {
return new ConfigurationInternal.Builder(userId)
.setUserConfigAllowed(true)
- .setAutoDetectionFeatureSupported(true)
+ .setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java
new file mode 100644
index 000000000000..5561b2c6a7aa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.TimeZone;
+
+@Presubmit
+public class ZoneInfoDbTimeZoneIdValidatorTest {
+ private final LocationTimeZoneProvider.TimeZoneIdValidator mTzChecker =
+ new ZoneInfoDbTimeZoneIdValidator();
+
+ @Test
+ public void timeZoneIdsFromZoneInfoDbAreValid() {
+ for (String timeZone : TimeZone.getAvailableIDs()) {
+ assertWithMessage("Time zone %s should be supported", timeZone)
+ .that(mTzChecker.isValid(timeZone)).isTrue();
+ }
+ }
+
+ @Test
+ public void nonExistingZones_areNotSupported() {
+ List<String> nonExistingTimeZones = Arrays.asList(
+ "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30"
+ );
+
+ for (String timeZone : nonExistingTimeZones) {
+ assertWithMessage(timeZone + " is not a valid time zone")
+ .that(mTzChecker.isValid(timeZone))
+ .isFalse();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 60390dc3995e..b2dacab26365 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -261,6 +261,7 @@ public class UsageStatsDatabaseTest {
// mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
+ assertEquals(us1.mLastTimeComponentUsed, us2.mLastTimeComponentUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible);
assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 2a3c2c46ce4e..b54b6969e7df 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -49,6 +49,8 @@ final class FakeVibratorControllerProvider {
private int mCapabilities;
private int[] mSupportedEffects;
private int[] mSupportedPrimitives;
+ private float mResonantFrequency;
+ private float mQFactor;
private final class FakeNativeWrapper extends VibratorController.NativeWrapper {
public int vibratorId;
@@ -89,6 +91,14 @@ final class FakeVibratorControllerProvider {
return mSupportedPrimitives;
}
+ public float getResonantFrequency() {
+ return mResonantFrequency;
+ }
+
+ public float getQFactor() {
+ return mQFactor;
+ }
+
public long perform(long effect, long strength, long vibrationId) {
if (mSupportedEffects == null
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
@@ -198,6 +208,16 @@ final class FakeVibratorControllerProvider {
mSupportedPrimitives = primitives;
}
+ /** Set the resonant frequency of the fake vibrator hardware. */
+ public void setResonantFrequency(float resonantFrequency) {
+ mResonantFrequency = resonantFrequency;
+ }
+
+ /** Set the Q factor of the fake vibrator hardware. */
+ public void setQFactor(float qFactor) {
+ mQFactor = qFactor;
+ }
+
/**
* Return the amplitudes set by this controller, including zeroes for each time the vibrator was
* turned off.
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 1b7e1ca6a014..7d5eec0834a1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -107,7 +107,8 @@ public class VibrationThreadTest {
waitForCompletion(thread);
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
- verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED));
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
+ eq(Vibration.Status.IGNORED_UNSUPPORTED));
}
@Test
@@ -121,7 +122,8 @@ public class VibrationThreadTest {
waitForCompletion(thread);
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
- verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED));
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
+ eq(Vibration.Status.IGNORED_UNSUPPORTED));
}
@Test
@@ -206,8 +208,8 @@ public class VibrationThreadTest {
thread.cancel();
waitForCompletion(thread);
- verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
- verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+ verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
+ verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index a28d18fb74d3..ce6639c6b4aa 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -266,6 +266,8 @@ public class VibratorManagerServiceTest {
vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
vibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
+ vibrator.setResonantFrequency(123.f);
+ vibrator.setQFactor(Float.NaN);
VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
assertNotNull(info);
@@ -279,6 +281,8 @@ public class VibratorManagerServiceTest {
info.isEffectSupported(VibrationEffect.EFFECT_TICK));
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
+ assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+ assertTrue(Float.isNaN(info.getQFactor()));
}
@Test
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 8789992280d0..799ec53a6e33 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -17,9 +17,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.servicestests.apps.simpleservicetestapp">
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
<application>
<service android:name=".SimpleService"
android:exported="true" />
+ <service android:name=".SimpleFgService"
+ android:exported="true" />
</application>
</manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS b/services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS
new file mode 100644
index 000000000000..72c0a9e6e90c
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/am/OWNERS
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
new file mode 100644
index 000000000000..ccfc0b7f0ef1
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.servicestests.apps.simpleservicetestapp;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.R;
+
+public class SimpleFgService extends Service {
+ private static final String TAG = SimpleFgService.class.getSimpleName();
+ private static final String NOTIFICATION_CHANNEL_ID = TAG;
+ private static final int NOTIFICATION_ID = 1;
+
+ private static final int MSG_INIT = 0;
+ private static final int MSG_DONE = 1;
+ private static final int MSG_START_FOREGROUND = 2;
+ private static final int MSG_STOP_FOREGROUND = 3;
+
+ private static final String ACTION_FGS_STATS_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+ private static final String EXTRA_MESSENGER = "extra_messenger";
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_FOREGROUND: {
+ Log.i(TAG, "startForeground");
+ startForeground(NOTIFICATION_ID, mNotification);
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
+ case MSG_STOP_FOREGROUND: {
+ Log.i(TAG, "stopForeground");
+ stopForeground(true);
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
+ }
+ }
+ };
+ private final Messenger mMessenger = new Messenger(mHandler);
+
+ private Notification mNotification;
+ private Messenger mRemoteMessenger;
+
+ @Override
+ public void onCreate() {
+ Log.i(TAG, "onCreate");
+ final NotificationManager nm = getSystemService(NotificationManager.class);
+ nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW));
+ mNotification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(TAG)
+ .setSmallIcon(R.drawable.ic_info)
+ .build();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand");
+ startForeground(NOTIFICATION_ID, mNotification);
+ if (ACTION_FGS_STATS_TEST.equals(intent.getAction())) {
+ mRemoteMessenger = new Messenger(intent.getExtras().getBinder(EXTRA_MESSENGER));
+ sendRemoteMessage(MSG_INIT, 0, 0, mMessenger);
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+ final Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ try {
+ mRemoteMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy");
+ mNotification = null;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 5182b3b69655..b921838e0bfc 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -243,10 +243,18 @@ public class ShortcutManagerTestUtils {
final UserHandle user = getParentUser(context);
List<String> roleHolders = callWithShellPermissionIdentity(
() -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
- if (roleHolders.size() == 1) {
+ int size = roleHolders.size();
+ if (size == 1) {
return roleHolders.get(0);
}
- fail("Failed to get the default launcher for user " + context.getUserId());
+
+ if (size > 1) {
+ fail("Too many launchers for user " + user.getIdentifier() + " using role "
+ + RoleManager.ROLE_HOME + ": " + roleHolders);
+ } else {
+ fail("No default launcher for user " + user.getIdentifier() + " using role "
+ + RoleManager.ROLE_HOME);
+ }
return null;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index e510b4fbfdd5..5462f47e3a4c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -61,6 +61,7 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.VibrationEffect;
@@ -81,10 +82,12 @@ import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.internal.util.IntPair;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LogicalLight;
+import com.android.server.pm.PackageManagerService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -412,12 +415,16 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
}
private void verifyVibrate() {
+ ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
- anyString(), any(AudioAttributes.class));
+ anyString(), captor.capture());
+ assertEquals(0, (captor.getValue().getAllFlags()
+ & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
}
private void verifyVibrate(int times) {
- verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(),
+ verify(mVibrator, times(times)).vibrate(eq(Process.SYSTEM_UID),
+ eq(PackageManagerService.PLATFORM_PACKAGE_NAME), any(), anyString(),
any(AudioAttributes.class));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d8e7582633de..c19f3489898d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -159,6 +159,11 @@ public class ActivityRecordTests extends WindowTestsBase {
setBooted(mAtm);
}
+ private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
+ return new TestStartingWindowOrganizer(mAtm,
+ mSystemServicesTestRule.getPowerManagerWrapper());
+ }
+
@Test
public void testStackCleanupOnClearingTask() {
final ActivityRecord activity = createActivityWith2LevelTask();
@@ -2294,6 +2299,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testCreateRemoveStartingWindow() {
+ registerTestStartingWindowOrganizer();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -2307,6 +2313,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testAddRemoveRace() {
+ registerTestStartingWindowOrganizer();
// There was once a race condition between adding and removing starting windows
final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
for (int i = 0; i < 1000; i++) {
@@ -2321,6 +2328,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testTransferStartingWindow() {
+ registerTestStartingWindowOrganizer();
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity1.addStartingWindow(mPackageName,
@@ -2337,9 +2345,10 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowWhileCreating() {
+ final TestStartingWindowOrganizer organizer = registerTestStartingWindowOrganizer();
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
- ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+ organizer.setRunnableWhenAddingSplashScreen(
() -> {
// Surprise, ...! Transfer window in the middle of the creation flow.
activity2.addStartingWindow(mPackageName,
@@ -2357,6 +2366,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowCanAnimate() {
+ registerTestStartingWindowOrganizer();
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity1.addStartingWindow(mPackageName,
@@ -2380,6 +2390,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowFromFinishingActivity() {
+ registerTestStartingWindowOrganizer();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task task = activity.getTask();
activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
@@ -2423,6 +2434,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowSetFixedRotation() {
+ registerTestStartingWindowOrganizer();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task task = activity.getTask();
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -2454,6 +2466,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testTryTransferStartingWindowFromHiddenAboveToken() {
+ registerTestStartingWindowOrganizer();
// Add two tasks on top of each other.
final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index f5d831bbe73e..67fe7bf436b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -117,6 +117,7 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.View;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -1699,6 +1700,20 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testFindScrollCaptureTargetWindow_cantReceiveKeys() {
+ DisplayContent display = createNewDisplay();
+ Task stack = createTaskStackOnDisplay(display);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+ WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
+ WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible");
+ invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
+
+ WindowState result = display.findScrollCaptureTargetWindow(null,
+ ActivityTaskManager.INVALID_TASK_ID);
+ assertEquals(activityWindow, result);
+ }
+
+ @Test
public void testFindScrollCaptureTargetWindow_taskId() {
DisplayContent display = createNewDisplay();
Task stack = createTaskStackOnDisplay(display);
@@ -1711,6 +1726,19 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() {
+ DisplayContent display = createNewDisplay();
+ Task stack = createTaskStackOnDisplay(display);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+ WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
+ window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
+ WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
+
+ WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
+ assertEquals(window, result);
+ }
+
+ @Test
public void testEnsureActivitiesVisibleNotRecursive() {
final TaskDisplayArea mockTda = mock(TaskDisplayArea.class);
final boolean[] called = { false };
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 8703c3103607..7f9e7da99579 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -154,7 +154,7 @@ public class DragDropControllerTests extends WindowTestsBase {
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
- any(InputChannel.class))).thenReturn(true);
+ any(InputChannel.class), any(boolean.class))).thenReturn(true);
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
@@ -370,7 +370,7 @@ public class DragDropControllerTests extends WindowTestsBase {
.build();
assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
- new InputChannel()));
+ new InputChannel(), true /* isDragDrop */));
mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data);
assertNotNull(mToken);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 983063125ce9..c483ae9fa4c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -141,7 +141,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
assertNull(mProvider.getControlTarget());
// We can have the control and the control target after seamless rotation.
- mProvider.finishSeamlessRotation(false /* timeout */);
+ mProvider.finishSeamlessRotation();
mProvider.updateControlForTarget(target, false /* force */);
assertNotNull(mProvider.getControl(target));
assertNotNull(mProvider.getControlTarget());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index c6be987802b5..7a4ad7410163 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -49,6 +52,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -511,6 +515,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
+ verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+ mDefaultDisplay.mDisplayId, false);
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
final WindowContainer parent = navToken.getParent();
@@ -518,6 +524,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
+ verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+ mDefaultDisplay.mDisplayId, true);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
verify(navBarFadeAnimationController).fadeWindowToken(true);
}
@@ -532,6 +540,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
+ verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+ mDefaultDisplay.mDisplayId, false);
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
final WindowContainer parent = navToken.getParent();
@@ -539,6 +549,51 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+ mDefaultDisplay.mDisplayId, true);
+ verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+ verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
+ }
+
+ @Test
+ public void testNotAttachNavigationBar_controlledByFixedRotationAnimation() {
+ setupForShouldAttachNavBarDuringTransition();
+ FixedRotationAnimationController mockController =
+ mock(FixedRotationAnimationController.class);
+ doReturn(mockController).when(mDefaultDisplay).getFixedRotationAnimationController();
+ final ActivityRecord homeActivity = createHomeActivity();
+ initializeRecentsAnimationController(mController, homeActivity);
+ assertFalse(mController.isNavigationBarAttachedToApp());
+ }
+
+ @Test
+ public void testAttachNavBarInSplitScreenMode() {
+ setupForShouldAttachNavBarDuringTransition();
+ final ActivityRecord primary = createActivityRecordWithParentTask(mDefaultDisplay,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord secondary = createActivityRecordWithParentTask(mDefaultDisplay,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord homeActivity = createHomeActivity();
+ homeActivity.setVisibility(true);
+ initializeRecentsAnimationController(mController, homeActivity);
+
+ WindowState navWindow = mController.getNavigationBarWindow();
+ final WindowToken navToken = navWindow.mToken;
+ final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
+
+ verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+ mDefaultDisplay.mDisplayId, false);
+ verify(navWindow).setSurfaceTranslationY(-secondary.getBounds().top);
+ verify(transaction).reparent(navToken.getSurfaceControl(), secondary.getSurfaceControl());
+ reset(navWindow);
+
+ mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ final WindowContainer parent = navToken.getParent();
+ final NavBarFadeAnimationController navBarFadeAnimationController =
+ mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
+ verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+ mDefaultDisplay.mDisplayId, true);
+ verify(navWindow).setSurfaceTranslationY(0);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
}
@@ -600,9 +655,10 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
private void setupForShouldAttachNavBarDuringTransition() {
mController.mShouldAttachNavBarToAppDuringTransition = true;
- final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ final WindowState navBar = spy(createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"));
mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
mWm.setRecentsAnimationController(mController);
+ doReturn(navBar).when(mController).getNavigationBarWindow();
final NavBarFadeAnimationController mockNavBarFadeAnimationController =
mock(NavBarFadeAnimationController.class);
final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2fdd63ed93d5..956c277e18c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -18,11 +18,13 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -34,8 +36,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -94,7 +101,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
mAdapter.setCallingPidUid(123, 456);
runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
- mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
+ mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler);
}
private WindowState createAppOverlayWindow() {
@@ -208,18 +215,20 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
@Test
- public void testZeroAnimations() {
+ public void testZeroAnimations() throws Exception {
mController.goodToGo(TRANSIT_OLD_NONE);
- verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+ verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
+ verify(mMockRunner).onAnimationCancelled();
}
@Test
- public void testNotReallyStarted() {
+ public void testNotReallyStarted() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+ verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
+ verify(mMockRunner).onAnimationCancelled();
}
@Test
@@ -250,7 +259,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
@Test
- public void testRemovedBeforeStarted() {
+ public void testRemovedBeforeStarted() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
@@ -258,7 +267,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mFinishedCallback);
win.mActivityRecord.removeImmediately();
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+ verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
+ verify(mMockRunner).onAnimationCancelled();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
}
@@ -520,6 +530,110 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
}
+ @Test
+ public void testNonAppTarget_sendNavBar() throws Exception {
+ final int transit = TRANSIT_OLD_TASK_OPEN;
+ final AnimationAdapter adapter = setupForNonAppTargetNavBar(transit, true);
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(transit), any(), any(),
+ nonAppsCaptor.capture(), finishedCaptor.capture());
+ boolean containNavTarget = false;
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ containNavTarget = true;
+ break;
+ }
+ }
+ assertTrue(containNavTarget);
+ assertEquals(1, mController.mPendingNonAppAnimations.size());
+ final NonAppWindowAnimationAdapter nonAppAdapter =
+ mController.mPendingNonAppAnimations.get(0);
+ spyOn(nonAppAdapter.getLeashFinishedCallback());
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
+ verify(nonAppAdapter.getLeashFinishedCallback())
+ .onAnimationFinished(nonAppAdapter.getLastAnimationType(), nonAppAdapter);
+ }
+
+ @Test
+ public void testNonAppTarget_notSendNavBar_notAttachToApp() throws Exception {
+ final int transit = TRANSIT_OLD_TASK_OPEN;
+ setupForNonAppTargetNavBar(transit, false);
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ verify(mMockRunner).onAnimationStart(eq(transit),
+ any(), any(), nonAppsCaptor.capture(), any());
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ fail("Non-app animation target must not contain navbar");
+ }
+ }
+ }
+
+ @Test
+ public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception {
+ final FixedRotationAnimationController mockController =
+ mock(FixedRotationAnimationController.class);
+ doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController();
+ final int transit = TRANSIT_OLD_TASK_OPEN;
+ setupForNonAppTargetNavBar(transit, true);
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ verify(mMockRunner).onAnimationStart(eq(transit),
+ any(), any(), nonAppsCaptor.capture(), any());
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ fail("Non-app animation target must not contain navbar");
+ }
+ }
+ }
+
+ @Test
+ public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception {
+ final RecentsAnimationController mockController =
+ mock(RecentsAnimationController.class);
+ doReturn(mockController).when(mWm).getRecentsAnimationController();
+ final int transit = TRANSIT_OLD_TASK_OPEN;
+ setupForNonAppTargetNavBar(transit, true);
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ verify(mMockRunner).onAnimationStart(eq(transit),
+ any(), any(), nonAppsCaptor.capture(), any());
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ fail("Non-app animation target must not contain navbar");
+ }
+ }
+ }
+
+ private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ spyOn(policy);
+ doReturn(shouldAttachNavBar).when(policy).shouldAttachNavBarToAppDuringTransition();
+
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(transit);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ return adapter;
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 171aa76032db..1b114c194bfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -116,8 +116,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
final Task rootTask = new TaskBuilder(mSupervisor).build();
final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true;
- // RootWindowContainer#invalidateTaskLayers should post to update.
- waitHandlerIdle(mWm.mH);
+ mWm.mRoot.rankTaskLayers();
assertEquals(1, task1.mLayerRank);
// Only tasks that directly contain activities have a ranking.
@@ -125,7 +124,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
final Task task2 = new TaskBuilder(mSupervisor).build();
new ActivityBuilder(mAtm).setTask(task2).build().mVisibleRequested = true;
- waitHandlerIdle(mWm.mH);
+ mWm.mRoot.rankTaskLayers();
// Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
// activities have the visible rank.
@@ -134,6 +133,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(1, task2.mLayerRank);
task2.moveToBack("test", null /* task */);
+ // RootWindowContainer#invalidateTaskLayers should post to update.
waitHandlerIdle(mWm.mH);
assertEquals(1, task1.mLayerRank);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 2f1d7eb404ad..5c7e58036aba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,6 +40,8 @@ import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -117,18 +119,19 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // create freeform display and a freeform app
+ // Create landscape freeform display and a freeform app.
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into freeform and then make it a compat app.
+ // Put app window into portrait freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
-
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertEquals(bounds, mActivity.getBounds());
+ // Activity is not yet in size compat mode; it is filling the freeform window.
+ assertMaxBoundsInheritDisplayAreaBounds();
// The activity should be able to accept negative x position [-150, 100 - 150, 600].
final int dx = bounds.left + bounds.width() / 2;
@@ -137,7 +140,7 @@ public class SizeCompatTests extends WindowTestsBase {
final int density = mActivity.getConfiguration().densityDpi;
- // change display configuration to fullscreen
+ // Change display configuration to fullscreen.
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -147,6 +150,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
+ // Size compat mode is sandboxed at the activity level.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -156,7 +161,8 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
final float aspectRatio = 1.2f;
- mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio;
+ mActivity.info.setMaxAspectRatio(aspectRatio);
+ mActivity.info.setMinAspectRatio(aspectRatio);
prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
@@ -172,6 +178,12 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxed();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -181,9 +193,17 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed.
+ assertActivityMaxBoundsSandboxed();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
+ // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
+ assertActivityMaxBoundsSandboxed();
+ // Activity max bounds ignore notch, since an app can be shown past the notch (although app
+ // is currently limited by the notch).
+ assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ .isEqualTo(displayBounds.height());
}
@Test
@@ -206,6 +226,9 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
+ // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
+ // max aspect ratio.
+ assertActivityMaxBoundsSandboxed();
assertScaled();
}
@@ -213,11 +236,13 @@ public class SizeCompatTests extends WindowTestsBase {
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
+ // Activity inherits bounds from TaskDisplayArea, since not sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -234,6 +259,8 @@ public class SizeCompatTests extends WindowTestsBase {
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxed();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -242,6 +269,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
+ // Activity is sandboxed to the offset size compat bounds.
+ assertActivityMaxBoundsSandboxed();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -261,6 +290,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
+ // Activity is sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -276,6 +307,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxed();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -287,6 +320,8 @@ public class SizeCompatTests extends WindowTestsBase {
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+ // Activity max bounds should be sandboxed since it is letterboxed.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -312,14 +347,13 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testMoveToDifferentOrientDisplay() {
+ public void testMoveToDifferentOrientationDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
- final int origWidth = configBounds.width();
- final int origHeight = configBounds.height();
+ final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -328,37 +362,45 @@ public class SizeCompatTests extends WindowTestsBase {
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(origWidth, configBounds.width());
- assertEquals(origHeight, configBounds.height());
+ assertEquals(originalBounds.width(), currentBounds.width());
+ assertEquals(originalBounds.height(), currentBounds.height());
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode on the new display.
+ assertActivityMaxBoundsSandboxed();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
+ (int) ((float) mActivity.getBounds().width() * originalBounds.height()
+ / originalBounds.width()));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), configBounds.height());
- assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- configBounds.width());
+ assertEquals(newDisplayBounds.height(), currentBounds.height());
+ assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ currentBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(configBounds.width(), appBounds.width());
- assertEquals(configBounds.height() - notchHeight, appBounds.height());
+ assertEquals(currentBounds.width(), appBounds.width());
+ assertEquals(currentBounds.height() - notchHeight, appBounds.height());
+ // Activity max bounds are sandboxed due to letterboxing from orientation mismatch with
+ // display.
+ assertActivityMaxBoundsSandboxed();
}
@Test
- public void testFixedOrientRotateCutoutDisplay() {
+ public void testFixedOrientationRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
+ final int width = 1000;
+ setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
+ // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
+ final float maxAspect = 1.4f;
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -366,6 +408,11 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
+ // Activity is sandboxed, and bounds include the area consumed by the notch.
+ assertActivityMaxBoundsSandboxed();
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
+ .isEqualTo(Math.round(width * maxAspect) + notchHeight);
+
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -381,10 +428,13 @@ public class SizeCompatTests extends WindowTestsBase {
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
+
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxed();
}
@Test
- public void testFixedAspOrientChangeOrient() {
+ public void testFixedAspectRatioOrientationChangeOrientation() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -396,6 +446,8 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxed();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -407,6 +459,8 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
+ // Activity is sandboxed due to fixed aspect ratio.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -455,6 +509,8 @@ public class SizeCompatTests extends WindowTestsBase {
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
+ // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
+ assertActivityMaxBoundsSandboxed();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -550,13 +606,13 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testShouldUseSizeCompatModeOnResizableTask() {
+ public void testShouldCreateCompatDisplayInsetsOnResizeableTask() {
setUpDisplaySizeWithApp(1000, 2500);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
- // Create a size compat activity on the same task.
+ // Create an activity on the same task.
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(mTask)
.setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
@@ -568,26 +624,112 @@ public class SizeCompatTests extends WindowTestsBase {
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldCreateCompatDisplayInsets());
+ // Activity should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldCreateCompatDisplayInsets());
+ // Activity should not be sandboxed.
+ assertMaxBoundsInheritDisplayAreaBounds();
+ }
+
+ @Test
+ public void testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesTrue() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+ .setSupportsSizeChanges(true)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
+ public void testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesFalse() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+ .setSupportsSizeChanges(false)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ assertTrue(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
+ public void testShouldCreateCompatDisplayInsetsWhenResizeableAndSupportsSizeChangesFalse() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_RESIZEABLE)
+ .setSupportsSizeChanges(false)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
+ public void
+ testShouldCreateCompatDisplayInsetsWhenUnfixedOrientationSupportsSizeChangesFalse() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+ .setSupportsSizeChanges(false)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
}
@Test
@EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
- public void testNoSizeCompatWhenPerAppOverrideSet() {
+ public void testShouldCreateCompatDisplayInsetsWhenForceResizeAppOverrideSet() {
setUpDisplaySizeWithApp(1000, 2500);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
- // Create a size compat activity on the same task.
+ // Create an activity on the same task.
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(mTask)
.setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+ .setSupportsSizeChanges(false)
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.setComponent(ComponentName.createRelative(mContext,
SizeCompatTests.class.getName()))
@@ -597,6 +739,181 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
+ public void testShouldCreateCompatDisplayInsetsWhenForceNonResizeOverrideSet() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_RESIZEABLE)
+ .setSupportsSizeChanges(true)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ assertTrue(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
+ public void testShouldCreateCompatDisplayInsetsWhenForceNonResizeSetAndUnfixedOrientation() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_RESIZEABLE)
+ .setSupportsSizeChanges(true)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ assertTrue(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioMedium() {
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioLowerThanManifest() {
+ setUpDisplaySizeWithApp(1400, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(2f)
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect, because the manifest aspect ratio is
+ // larger (2:1)
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(800, activity.getBounds().width());
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioLargerThanManifest() {
+ setUpDisplaySizeWithApp(1400, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(1.1f)
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect, because the manifest aspect ratio is
+ // larger (2:1)
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioLarge() {
+ setUpDisplaySizeWithApp(1500, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 16:9 aspect ratio
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatio_Both() {
+ // If multiple override aspect ratios are set, we should use the largest one
+
+ setUpDisplaySizeWithApp(1400, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 16:9 aspect ratio
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioWithoutGlobalOverride() {
+ // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
+ // OVERRIDE_MIN_ASPECT_RATIO being also set.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1000, activity.getBounds().width());
+ }
+
+ @Test
public void testLaunchWithFixedRotationTransform() {
final int dw = 1000;
final int dh = 2500;
@@ -637,6 +954,9 @@ public class SizeCompatTests extends WindowTestsBase {
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ // Activity is sandboxed.
+ assertActivityMaxBoundsSandboxed();
+
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -646,6 +966,7 @@ public class SizeCompatTests extends WindowTestsBase {
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -668,6 +989,7 @@ public class SizeCompatTests extends WindowTestsBase {
// App should launch in fixed orientation letterbox.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
// Activity bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), activityBounds.height());
@@ -684,7 +1006,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
// orientation letterbox.
mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f);
- mActivity.info.minAspectRatio = 3;
+ mActivity.info.setMinAspectRatio(3);
prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
@@ -701,7 +1023,8 @@ public class SizeCompatTests extends WindowTestsBase {
// Activity bounds should respect minimum aspect ratio for activity.
assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio),
+ assertEquals((int) Math.rint(displayBounds.height()
+ / mActivity.info.getManifestMinAspectRatio()),
activityBounds.width());
}
@@ -730,7 +1053,8 @@ public class SizeCompatTests extends WindowTestsBase {
// Activity bounds should respect maximum aspect ratio for activity.
assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio),
+ assertEquals((int) Math.rint(displayBounds.height()
+ / mActivity.info.getMaxAspectRatio()),
activityBounds.width());
}
@@ -789,6 +1113,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -800,29 +1125,29 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- Rect activityBounds = new Rect(mActivity.getBounds());
-
// App should launch in fullscreen.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
- assertEquals(displayBounds, activityBounds);
+ // Activity inherits max bounds from TaskDisplayArea.
+ assertMaxBoundsInheritDisplayAreaBounds();
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- activityBounds = new Rect(mActivity.getBounds());
- assertTrue(displayBounds.width() > displayBounds.height());
+ final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
+ assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ assertActivityMaxBoundsSandboxed();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
- activityBounds.width());
+ assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
+ assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
+ / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
}
@Test
@@ -859,6 +1184,7 @@ public class SizeCompatTests extends WindowTestsBase {
// has 700x1400 bounds with the ratio as the display.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
assertEquals(taskBounds, displayBounds);
assertEquals(displayBounds.height(), newActivityBounds.height());
assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
@@ -899,11 +1225,17 @@ public class SizeCompatTests extends WindowTestsBase {
// Task bounds should fill parent bounds.
assertEquals(displayBounds, taskBounds);
+ // Prior and new activity max bounds are sandboxed due to letterbox.
+ assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(newActivityBounds);
+ assertActivityMaxBoundsSandboxed();
+
// Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(displayBounds.height(), newActivityBounds.height());
- assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio),
+ assertEquals((long) Math.rint(newActivityBounds.height()
+ / newActivity.info.getMaxAspectRatio()),
newActivityBounds.width());
}
@@ -927,6 +1259,9 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertThat(mActivity.inSizeCompatMode()).isTrue();
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertActivityMaxBoundsSandboxed();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -936,6 +1271,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
assertEquals(activityBounds, mActivity.getBounds());
+ // Activity max bounds are sandboxed due to size compat.
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -951,6 +1288,7 @@ public class SizeCompatTests extends WindowTestsBase {
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -958,13 +1296,15 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertActivityMaxBoundsSandboxed();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
- // In Task letterbox
+ // In activity letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -982,20 +1322,23 @@ public class SizeCompatTests extends WindowTestsBase {
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
+ assertActivityMaxBoundsSandboxed();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_180);
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
+ assertActivityMaxBoundsSandboxed();
}
@Test
@@ -1012,12 +1355,18 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- taskDisplayArea.setBounds(0, 0, 2400, 1000);
+ Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
+ taskDisplayArea.setBounds(displayAreaBounds);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
+ // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
+ assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(displayAreaBounds);
}
@Test
@@ -1042,6 +1391,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
assertEquals(originalBounds,
mActivity.getConfiguration().windowConfiguration.getBounds());
+ assertActivityMaxBoundsSandboxed();
// Recompute the natural configuration of the non-resizable activity and the split screen.
mActivity.clearSizeCompatMode();
@@ -1053,12 +1403,13 @@ public class SizeCompatTests extends WindowTestsBase {
addWindowToActivity(mActivity);
mActivity.mRootWindowContainer.performSurfacePlacement();
- // Split screen is also in portrait [1000,1400], so activty should be in fixed orientation
+ // Split screen is also in portrait [1000,1400], so activity should be in fixed orientation
// letterbox.
assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
assertFitted();
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertActivityMaxBoundsSandboxed();
// Letterbox should fill the gap between the split screen and the letterboxed activity.
final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
@@ -1133,7 +1484,7 @@ public class SizeCompatTests extends WindowTestsBase {
: RESIZE_MODE_RESIZEABLE;
activity.mVisibleRequested = true;
if (maxAspect >= 0) {
- activity.info.maxAspectRatio = maxAspect;
+ activity.info.setMaxAspectRatio(maxAspect);
}
if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
activity.info.screenOrientation = screenOrientation;
@@ -1165,6 +1516,22 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.hasSizeCompatBounds());
}
+ /** Asserts the activity max bounds inherit from the TaskDisplayArea. */
+ private void assertMaxBoundsInheritDisplayAreaBounds() {
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mTask.getDisplayArea().getBounds());
+ }
+
+ /**
+ * Asserts activity-level letterbox or size compat mode size compat mode, so activity max
+ * bounds are sandboxed.
+ */
+ private void assertActivityMaxBoundsSandboxed() {
+ // Activity max bounds are sandboxed due to size compat mode.
+ assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
+ .isEqualTo(mActivity.getWindowConfiguration().getBounds());
+ }
+
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e22cda60d423..eba5634e4355 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -42,16 +42,20 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.app.ActivityOptions;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -87,6 +91,48 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
}
@Test
+ public void getOrCreateLaunchRootRespectsResolvedWindowingMode() {
+ final Task rootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ rootTask.mCreatedByOrganizer = true;
+ final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
+ taskDisplayArea.setLaunchRootTask(
+ rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+ final Task candidateRootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+ final LaunchParams launchParams = new LaunchParams();
+ launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
+
+ final Task actualRootTask = taskDisplayArea.getOrCreateRootTask(
+ activity, null /* options */, candidateRootTask,
+ launchParams, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertSame(rootTask, actualRootTask.getRootTask());
+ }
+
+ @Test
+ public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() {
+ final Task rootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ rootTask.mCreatedByOrganizer = true;
+ final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
+ taskDisplayArea.setLaunchRootTask(
+ rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+ final Task candidateRootTask = createTaskStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ final Task actualRootTask = taskDisplayArea.getOrCreateRootTask(
+ activity, options, candidateRootTask,
+ null /* launchParams */, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertSame(rootTask, actualRootTask.getRootTask());
+ }
+
+ @Test
public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 75226b7e66f7..8bc4cedf6fce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -34,7 +34,6 @@ import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
import android.view.InputChannel;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -63,7 +62,8 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
when(mWm.mInputManager.transferTouchFocus(
any(InputChannel.class),
- any(InputChannel.class))).thenReturn(true);
+ any(InputChannel.class),
+ any(boolean.class))).thenReturn(true);
mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index ecb8b607a106..dca6b089d66b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.google.common.truth.Truth.assertThat;
@@ -277,4 +278,15 @@ public class TaskTests extends WindowTestsBase {
// Orientation request from standard activity in multi window will not be handled.
assertFalse(leafTask2.handlesOrientationChangeFromDescendant());
}
+
+ @Test
+ public void testAlwaysOnTop() {
+ final Task task = createTaskStackOnDisplay(mDisplayContent);
+ task.setAlwaysOnTop(true);
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ assertTrue(task.isAlwaysOnTop());
+
+ task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
+ assertFalse(task.isAlwaysOnTop());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ae85ceb72958..21536a6e1cfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,6 +136,10 @@ class TestDisplayContent extends DisplayContent {
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
+ // Ensure letterbox aspect ratio is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
+ // the below method, is set on some device form factors.
+ mService.mWindowManager.setFixedOrientationLetterboxAspectRatio(0);
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 001afe3e2637..b3a07454c1a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -22,10 +22,11 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.MergedConfiguration;
import android.view.DragEvent;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindow;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.ScrollCaptureResponse;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -116,7 +117,15 @@ public class TestIWindow extends IWindow.Stub {
}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) throws RemoteException {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener)
+ throws RemoteException {
+ try {
+ listener.onScrollCaptureResponse(
+ new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build());
+
+ } catch (RemoteException ex) {
+ // ignore
+ }
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 86d8eee878fd..ae8e2ded359d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -100,9 +100,9 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
- int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+ public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+ int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+ int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
final com.android.server.wm.WindowState window;
final ActivityRecord activity;
final WindowManagerService wm = mWmSupplier.get();
@@ -119,7 +119,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
mRunnableWhenAddingSplashScreen.run();
mRunnableWhenAddingSplashScreen = null;
}
- return () -> {
+ return (a) -> {
synchronized (wm.mGlobalLock) {
activity.removeChild(window);
activity.mStartingWindow = null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 2c2c09a5750a..01c503e01326 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -547,7 +547,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Override
- public void removeStartingWindow(int taskId) { }
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) { }
@Override
public void copySplashScreenView(int taskId) { }
@@ -614,7 +615,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Override
- public void removeStartingWindow(int taskId) { }
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) { }
@Override
public void copySplashScreenView(int taskId) { }
@Override
@@ -688,7 +690,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Override
- public void removeStartingWindow(int taskId) { }
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) { }
@Override
public void copySplashScreenView(int taskId) { }
@Override
@@ -832,7 +835,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
@Override
- public void removeStartingWindow(int taskId) { }
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) { }
@Override
public void copySplashScreenView(int taskId) { }
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 51aec65f7285..5b5b1da327bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -521,7 +521,7 @@ public class WindowStateTests extends WindowTestsBase {
matrix.mapPoints(curSurfacePos);
verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
- app.finishSeamlessRotation(false /* timeout */);
+ app.finishSeamlessRotation(t);
assertFalse(app.mSeamlesslyRotated);
assertNull(app.mPendingSeamlessRotate);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 8d50a77447da..779457bb3e2f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -50,6 +51,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -67,6 +69,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Bundle;
@@ -74,6 +77,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.IDisplayWindowInsetsController;
@@ -100,6 +104,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.HashMap;
/** Common base class for window manager unit test classes. */
class WindowTestsBase extends SystemServiceTestsBase {
@@ -176,6 +181,11 @@ class WindowTestsBase extends SystemServiceTestsBase {
} else {
mDisplayContent = mDefaultDisplay;
}
+
+ // Ensure letterbox aspect ratio is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set
+ // on some device form factors.
+ mAtm.mWindowManager.setFixedOrientationLetterboxAspectRatio(0);
}
private void createTestDisplay(UseTestDisplay annotation) {
@@ -704,6 +714,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
private int mLaunchMode;
private int mResizeMode = RESIZE_MODE_RESIZEABLE;
private float mMaxAspectRatio;
+ private float mMinAspectRatio;
+ private boolean mSupportsSizeChanges;
private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
private boolean mLaunchTaskBehind = false;
private int mConfigChanges;
@@ -782,6 +794,16 @@ class WindowTestsBase extends SystemServiceTestsBase {
return this;
}
+ ActivityBuilder setMinAspectRatio(float minAspectRatio) {
+ mMinAspectRatio = minAspectRatio;
+ return this;
+ }
+
+ ActivityBuilder setSupportsSizeChanges(boolean supportsSizeChanges) {
+ mSupportsSizeChanges = supportsSizeChanges;
+ return this;
+ }
+
ActivityBuilder setScreenOrientation(int screenOrientation) {
mScreenOrientation = screenOrientation;
return this;
@@ -868,7 +890,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
aInfo.flags |= mActivityFlags;
aInfo.launchMode = mLaunchMode;
aInfo.resizeMode = mResizeMode;
- aInfo.maxAspectRatio = mMaxAspectRatio;
+ aInfo.setMaxAspectRatio(mMaxAspectRatio);
+ aInfo.setMinAspectRatio(mMinAspectRatio);
+ aInfo.supportsSizeChanges = mSupportsSizeChanges;
aInfo.screenOrientation = mScreenOrientation;
aInfo.configChanges |= mConfigChanges;
aInfo.taskAffinity = mAffinity;
@@ -1111,6 +1135,88 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
}
+ static class TestStartingWindowOrganizer extends ITaskOrganizer.Stub {
+ private final ActivityTaskManagerService mAtm;
+ private final WindowManagerService mWMService;
+ private final WindowState.PowerManagerWrapper mPowerManagerWrapper;
+
+ private Runnable mRunnableWhenAddingSplashScreen;
+ private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>();
+ private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>();
+
+ TestStartingWindowOrganizer(ActivityTaskManagerService service,
+ WindowState.PowerManagerWrapper powerManagerWrapper) {
+ mAtm = service;
+ mWMService = mAtm.mWindowManager;
+ mPowerManagerWrapper = powerManagerWrapper;
+ if (DEBUG_ENABLE_SHELL_DRAWER) {
+ mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
+ mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
+ }
+ }
+
+ void setRunnableWhenAddingSplashScreen(Runnable r) {
+ if (DEBUG_ENABLE_SHELL_DRAWER) {
+ mRunnableWhenAddingSplashScreen = r;
+ } else {
+ ((TestWindowManagerPolicy) mWMService.mPolicy).setRunnableWhenAddingSplashScreen(r);
+ }
+ }
+
+ @Override
+ public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
+ synchronized (mWMService.mGlobalLock) {
+ final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
+ appToken);
+ IWindow iWindow = mock(IWindow.class);
+ doReturn(mock(IBinder.class)).when(iWindow).asBinder();
+ final WindowState window = WindowTestsBase.createWindow(null,
+ TYPE_APPLICATION_STARTING, activity,
+ "Starting window", 0 /* ownerId */, 0 /* userId*/,
+ false /* internalWindows */, mWMService, mock(Session.class),
+ iWindow,
+ mPowerManagerWrapper);
+ activity.mStartingWindow = window;
+ mAppWindowMap.put(appToken, window);
+ mTaskAppMap.put(info.taskInfo.taskId, appToken);
+ }
+ if (mRunnableWhenAddingSplashScreen != null) {
+ mRunnableWhenAddingSplashScreen.run();
+ mRunnableWhenAddingSplashScreen = null;
+ }
+ }
+ @Override
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
+ synchronized (mWMService.mGlobalLock) {
+ final IBinder appToken = mTaskAppMap.get(taskId);
+ if (appToken != null) {
+ mTaskAppMap.remove(taskId);
+ final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
+ appToken);
+ WindowState win = mAppWindowMap.remove(appToken);
+ activity.removeChild(win);
+ activity.mStartingWindow = null;
+ }
+ }
+ }
+ @Override
+ public void copySplashScreenView(int taskId) {
+ }
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
+ }
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
+ }
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ }
+ @Override
+ public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+ }
+ }
+
static class TestSplitOrganizer extends ITaskOrganizer.Stub {
final ActivityTaskManagerService mService;
Task mPrimary;
@@ -1149,7 +1255,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
}
@Override
- public void removeStartingWindow(int taskId) {
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ boolean playRevealAnimation) {
}
@Override
public void copySplashScreenView(int taskId) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index c82ba995f12e..d967891fdb76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -470,7 +470,7 @@ public class ZOrderingTests extends WindowTestsBase {
mWm, mockRunner, null, displayId);
spyOn(controller);
controller.mShouldAttachNavBarToAppDuringTransition = true;
- doReturn(mNavBarWindow.mToken).when(controller).getNavigationBarWindowToken();
+ doReturn(mNavBarWindow).when(controller).getNavigationBarWindow();
mWm.setRecentsAnimationController(controller);
// set ime visible
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index 8874e0afd716..72e1e33491ff 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -203,6 +204,28 @@ public final class TranslationManagerService
}
}
+ @Override
+ public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+ TranslationManagerServiceImpl service;
+ synchronized (mLock) {
+ service = getServiceForUserLocked(userId);
+ }
+ if (service != null) {
+ service.registerUiTranslationStateCallback(callback, Binder.getCallingUid());
+ }
+ }
+
+ @Override
+ public void unregisterUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+ TranslationManagerServiceImpl service;
+ synchronized (mLock) {
+ service = getServiceForUserLocked(userId);
+ }
+ if (service != null) {
+ service.unregisterUiTranslationStateCallback(callback);
+ }
+ }
+
/**
* Dump the service state into the given stream. You run "adb shell dumpsys translation".
*/
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index ab6ac12c90fa..1ca07cb8d928 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,17 +17,24 @@
package com.android.server.translation;
import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS;
+import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
+import static android.view.translation.UiTranslationManager.EXTRA_STATE;
+import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.service.translation.TranslationServiceInfo;
import android.util.Slog;
import android.view.autofill.AutofillId;
+import android.view.inputmethod.InputMethodInfo;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationManager.UiTranslationState;
@@ -36,6 +43,7 @@ import com.android.internal.os.IResultReceiver;
import com.android.internal.util.SyncResultReceiver;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
@@ -174,5 +182,50 @@ final class TranslationManagerServiceImpl extends
} catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e);
}
+ invokeCallbacks(state, sourceSpec, destSpec);
}
+
+ private void invokeCallbacks(
+ int state, TranslationSpec sourceSpec, TranslationSpec targetSpec) {
+ Bundle res = new Bundle();
+ res.putInt(EXTRA_STATE, state);
+ // TODO(177500482): Store the locale pair so it can be sent for RESUME events.
+ if (sourceSpec != null) {
+ res.putString(EXTRA_SOURCE_LOCALE, sourceSpec.getLanguage());
+ res.putString(EXTRA_TARGET_LOCALE, targetSpec.getLanguage());
+ }
+ // TODO(177500482): Only support the *current* Input Method.
+ List<InputMethodInfo> enabledInputMethods =
+ LocalServices.getService(InputMethodManagerInternal.class)
+ .getEnabledInputMethodListAsUser(mUserId);
+ mCallbacks.broadcast((callback, uid) -> {
+ // Code here is non-optimal since it's temporary..
+ boolean isIme = false;
+ for (InputMethodInfo inputMethod : enabledInputMethods) {
+ if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) {
+ isIme = true;
+ }
+ }
+ // TODO(177500482): Invoke it for the application being translated too.
+ if (!isIme) {
+ return;
+ }
+ try {
+ callback.sendResult(res);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e);
+ }
+ });
+ }
+
+ public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) {
+ mCallbacks.register(callback, sourceUid);
+ // TODO(177500482): trigger the callback here if we're already translating the UI.
+ }
+
+ public void unregisterUiTranslationStateCallback(IRemoteCallback callback) {
+ mCallbacks.unregister(callback);
+ }
+
+ private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 78b14779d6b3..ec4c5fc4a846 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -147,6 +147,10 @@ final class UsageStatsProto {
stats.mTotalTimeVisible = proto.readLong(
IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS);
break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS:
+ stats.mLastTimeComponentUsed = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS);
+ break;
}
}
proto.end(token);
@@ -345,6 +349,9 @@ final class UsageStatsProto {
usageStats.mLastTimeVisible, stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
usageStats.mTotalTimeVisible);
+ UsageStatsProtoV2.writeOffsetTimestamp(proto,
+ IntervalStatsProto.UsageStats.LAST_TIME_COMPONENT_USED_MS,
+ usageStats.mLastTimeComponentUsed, stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
try {
writeChooserCounts(proto, usageStats);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index e6d28417d3ca..5c5667cf0389 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -90,6 +90,10 @@ final class UsageStatsProtoV2 {
stats.mTotalTimeVisible = proto.readLong(
UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS);
break;
+ case (int) UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS:
+ stats.mLastTimeComponentUsed = beginTime + proto.readLong(
+ UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return stats;
}
@@ -312,6 +316,8 @@ final class UsageStatsProtoV2 {
writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
stats.mLastTimeVisible, beginTime);
proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
+ writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS,
+ stats.mLastTimeComponentUsed, beginTime);
proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
try {
writeChooserCounts(proto, stats);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f35b9e2ce0ed..22b4f4ef57e5 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1003,6 +1003,8 @@ class UserUsageStatsService {
formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
pw.printPair("lastTimeVisible",
formatDateTime(usageStats.mLastTimeVisible, prettyDates));
+ pw.printPair("lastTimeComponentUsed",
+ formatDateTime(usageStats.mLastTimeComponentUsed, prettyDates));
pw.printPair("totalTimeFS",
formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
pw.printPair("lastTimeFS",
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index ed71d17b0dde..e089995a7b1c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -17,6 +17,7 @@
package com.android.server.voiceinteraction;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -25,8 +26,10 @@ import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioAttributes;
import android.media.AudioRecord;
import android.media.MediaRecorder;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SharedMemory;
import android.service.voice.AlwaysOnHotwordDetector;
import android.service.voice.HotwordDetectionService;
import android.service.voice.IDspHotwordDetectionCallback;
@@ -74,7 +77,8 @@ final class HotwordDetectionConnection {
boolean mBound;
HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName,
- int userId, boolean bindInstantServiceAllowed) {
+ int userId, boolean bindInstantServiceAllowed, @Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory) {
mLock = lock;
mContext = context;
mDetectionComponentName = serviceName;
@@ -90,6 +94,14 @@ final class HotwordDetectionConnection {
boolean connected) {
synchronized (mLock) {
mBound = connected;
+ if (connected) {
+ try {
+ service.setConfig(options, sharedMemory);
+ } catch (RemoteException e) {
+ // TODO: (b/181842909) Report an error to voice interactor
+ Slog.w(TAG, "Failed to setConfig for HotwordDetectionService", e);
+ }
+ }
}
}
@@ -117,6 +129,11 @@ final class HotwordDetectionConnection {
}
}
+ void setConfigLocked(Bundle options, SharedMemory sharedMemory) {
+ mRemoteHotwordDetectionService.run(
+ service -> service.setConfig(options, sharedMemory));
+ }
+
private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
IHotwordRecognitionStatusCallback externalCallback) {
if (DEBUG) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index dce63ebb0889..80d4f8fd0c2f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -60,6 +60,7 @@ import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SharedMemory;
import android.os.ShellCallback;
import android.os.Trace;
import android.os.UserHandle;
@@ -982,23 +983,47 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
- public int setHotwordDetectionConfig(Bundle options) {
+ public void setHotwordDetectionServiceConfig(@Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory) {
+ enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION);
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
if (mImpl == null) {
Slog.w(TAG,
- "setHotwordDetectionConfig without running voice interaction service");
- return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+ "setHotwordDetectionServiceConfig without running voice"
+ + " interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.setHotwordDetectionServiceConfigLocked(options, sharedMemory);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public void shutdownHotwordDetectionService() {
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService();
+
+ if (mImpl == null) {
+ Slog.w(TAG,
+ "shutdownHotwordDetectionService without running voice"
+ + " interaction service");
+ return;
}
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.setHotwordDetectionConfigLocked(options);
+ mImpl.shutdownHotwordDetectionServiceLocked();
} finally {
Binder.restoreCallingIdentity(caller);
}
}
}
+
//----------------- Model management APIs --------------------------------//
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 04dea3f7a2c6..58616104755d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -43,11 +43,13 @@ import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SharedMemory;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
+import android.system.OsConstants;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.view.IWindowManager;
@@ -389,25 +391,48 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
return mInfo.getSupportsLocalInteraction();
}
- public int setHotwordDetectionConfigLocked(Bundle options) {
+ public void setHotwordDetectionServiceConfigLocked(@Nullable Bundle options,
+ @Nullable SharedMemory sharedMemory) {
if (DEBUG) {
- Slog.d(TAG, "setHotwordDetectionConfigLocked");
+ Slog.d(TAG, "setHotwordDetectionServiceConfigLocked");
}
if (mHotwordDetectionComponentName == null) {
- Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
- + " name not found");
- return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+ Slog.w(TAG, "Hotword detection service name not found");
+ throw new IllegalStateException("Hotword detection service name not found");
}
if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
- return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+ Slog.w(TAG, "Hotword detection service not in isolated process");
+ throw new IllegalStateException("Hotword detection service not in isolated process");
}
// TODO : Need to check related permissions for hotword detection service
// TODO : Sanitize for bundle
- mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
- mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false);
+ if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
+ Slog.w(TAG, "Can't set sharedMemory to be read-only");
+ throw new IllegalStateException("Can't set sharedMemory to be read-only");
+ }
+
+ if (mHotwordDetectionConnection == null) {
+ mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
+ mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
+ options, sharedMemory);
+ } else {
+ mHotwordDetectionConnection.setConfigLocked(options, sharedMemory);
+ }
+ }
+
+ public void shutdownHotwordDetectionServiceLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "shutdownHotwordDetectionServiceLocked");
+ }
+
+ if (mHotwordDetectionConnection == null) {
+ Slog.w(TAG, "shutdown, but no hotword detection connection");
+ return;
+ }
- return VoiceInteractionService.HOTWORD_CONFIG_SUCCESS;
+ mHotwordDetectionConnection.cancelLocked();
+ mHotwordDetectionConnection = null;
}
public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
index 201c5db74e16..f5357b19c4de 100644
--- a/telecomm/java/android/telecom/CallDiagnosticService.java
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -19,9 +19,12 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -30,6 +33,7 @@ import com.android.internal.telecom.ICallDiagnosticService;
import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
import java.util.Map;
+import java.util.concurrent.Executor;
/**
* The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the
@@ -51,6 +55,11 @@ import java.util.Map;
* </service>
* }
* </pre>
+ * <p>
+ * <h2>Threading Model</h2>
+ * By default, all incoming IPC from Telecom in this service and in the {@link CallDiagnostics}
+ * instances will take place on the main thread. You can override {@link #getExecutor()} in your
+ * implementation to provide your own {@link Executor}.
* @hide
*/
@SystemApi
@@ -83,7 +92,7 @@ public abstract class CallDiagnosticService extends Service {
@Override
public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException {
- onCallAudioStateChanged(callAudioState);
+ getExecutor().execute(() -> onCallAudioStateChanged(callAudioState));
}
@Override
@@ -96,29 +105,37 @@ public abstract class CallDiagnosticService extends Service {
throws RemoteException {
handleBluetoothCallQualityReport(qualityReport);
}
+
+ @Override
+ public void notifyCallDisconnected(@NonNull String callId,
+ @NonNull DisconnectCause disconnectCause) throws RemoteException {
+ handleCallDisconnected(callId, disconnectCause);
+ }
}
/**
- * Listens to events raised by a {@link DiagnosticCall}.
+ * Listens to events raised by a {@link CallDiagnostics}.
*/
- private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener =
- new android.telecom.DiagnosticCall.Listener() {
+ private CallDiagnostics.Listener mDiagnosticCallListener =
+ new CallDiagnostics.Listener() {
@Override
- public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall,
- @DiagnosticCall.MessageType int message, int value) {
- handleSendDeviceToDeviceMessage(diagnosticCall, message, value);
+ public void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics,
+ @CallDiagnostics.MessageType int message, int value) {
+ handleSendDeviceToDeviceMessage(callDiagnostics, message, value);
}
@Override
- public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ public void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics,
+ int messageId,
CharSequence message) {
- handleDisplayDiagnosticMessage(diagnosticCall, messageId, message);
+ handleDisplayDiagnosticMessage(callDiagnostics, messageId, message);
}
@Override
- public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
- handleClearDiagnosticMessage(diagnosticCall, messageId);
+ public void onClearDiagnosticMessage(CallDiagnostics callDiagnostics,
+ int messageId) {
+ handleClearDiagnosticMessage(callDiagnostics, messageId);
}
};
@@ -132,9 +149,19 @@ public abstract class CallDiagnosticService extends Service {
* Map which tracks the Telecom calls received from the Telecom stack.
*/
private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>();
- private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+ private final Map<String, CallDiagnostics> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+ private final Object mLock = new Object();
private ICallDiagnosticServiceAdapter mAdapter;
+ /**
+ * Handles binding to the {@link CallDiagnosticService}.
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link android.content.Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ * @return
+ */
@Nullable
@Override
public IBinder onBind(@NonNull Intent intent) {
@@ -143,32 +170,57 @@ public abstract class CallDiagnosticService extends Service {
}
/**
+ * Returns the {@link Executor} to use for incoming IPS from Telecom into your service
+ * implementation.
+ * <p>
+ * Override this method in your {@link CallDiagnosticService} implementation to provide the
+ * executor you want to use for incoming IPC.
+ *
+ * @return the {@link Executor} to use for incoming IPC from Telecom to
+ * {@link CallDiagnosticService} and {@link CallDiagnostics}.
+ */
+ @SuppressLint("OnNameExpected")
+ @NonNull public Executor getExecutor() {
+ return new HandlerExecutor(Handler.createAsync(getMainLooper()));
+ }
+
+ /**
* Telecom calls this method on the {@link CallDiagnosticService} with details about a new call
* which was added to Telecom.
* <p>
- * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be
+ * The {@link CallDiagnosticService} returns an implementation of {@link CallDiagnostics} to be
* used for the lifespan of this call.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
*
* @param call The details of the new call.
- * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
+ * @return An instance of {@link CallDiagnostics} which the {@link CallDiagnosticService}
* provides to be used for the lifespan of the call.
- * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned.
+ * @throws IllegalArgumentException if a {@code null} {@link CallDiagnostics} is returned.
*/
- public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull
+ public abstract @NonNull CallDiagnostics onInitializeCallDiagnostics(@NonNull
android.telecom.Call.Details call);
/**
- * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed.
- * This happens when Telecom is no longer tracking the call in question.
+ * Telecom calls this method when a previous created {@link CallDiagnostics} is no longer
+ * needed. This happens when Telecom is no longer tracking the call in question.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
+ *
* @param call The diagnostic call which is no longer tracked by Telecom.
*/
- public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
+ public abstract void onRemoveCallDiagnostics(@NonNull CallDiagnostics call);
/**
* Telecom calls this method when the audio routing or available audio route information
* changes.
* <p>
* Audio state is common to all calls.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
*
* @param audioState The new audio state.
*/
@@ -178,6 +230,10 @@ public abstract class CallDiagnosticService extends Service {
/**
* Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the
* bluetooth stack.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
+ *
* @param qualityReport the {@link BluetoothCallQualityReport}.
*/
public abstract void onBluetoothCallQualityReportReceived(
@@ -199,31 +255,40 @@ public abstract class CallDiagnosticService extends Service {
String telecomCallId = parcelableCall.getId();
Log.i(this, "handleCallAdded: callId=%s - added", telecomCallId);
Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
- mCallByTelecomCallId.put(telecomCallId, newCallDetails);
-
- DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
- if (diagnosticCall == null) {
- throw new IllegalArgumentException("A valid DiagnosticCall instance was not provided.");
+ synchronized (mLock) {
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
}
- diagnosticCall.setListener(mDiagnosticCallListener);
- diagnosticCall.setCallId(telecomCallId);
- mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+
+ getExecutor().execute(() -> {
+ CallDiagnostics callDiagnostics = onInitializeCallDiagnostics(newCallDetails);
+ if (callDiagnostics == null) {
+ throw new IllegalArgumentException(
+ "A valid DiagnosticCall instance was not provided.");
+ }
+ synchronized (mLock) {
+ callDiagnostics.setListener(mDiagnosticCallListener);
+ callDiagnostics.setCallId(telecomCallId);
+ mDiagnosticCallByTelecomCallId.put(telecomCallId, callDiagnostics);
+ }
+ });
}
/**
* Handles an update to {@link Call.Details} notified by Telecom.
- * Caches the call details and notifies the {@link DiagnosticCall} of the change via
- * {@link DiagnosticCall#onCallDetailsChanged(Call.Details)}.
+ * Caches the call details and notifies the {@link CallDiagnostics} of the change via
+ * {@link CallDiagnostics#onCallDetailsChanged(Call.Details)}.
* @param parcelableCall the new parceled call details from Telecom.
*/
private void handleCallUpdated(@NonNull ParcelableCall parcelableCall) {
String telecomCallId = parcelableCall.getId();
Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId);
Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
-
- DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
- mCallByTelecomCallId.put(telecomCallId, newCallDetails);
- diagnosticCall.handleCallUpdated(newCallDetails);
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+ }
+ getExecutor().execute(() -> callDiagnostics.handleCallUpdated(newCallDetails));
}
/**
@@ -236,24 +301,65 @@ public abstract class CallDiagnosticService extends Service {
if (mCallByTelecomCallId.containsKey(telecomCallId)) {
mCallByTelecomCallId.remove(telecomCallId);
}
- if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
- DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
- // Inform the service of the removed call.
- onRemoveDiagnosticCall(call);
+
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+ } else {
+ callDiagnostics = null;
+ }
+ }
+
+ // Inform the service of the removed call.
+ if (callDiagnostics != null) {
+ getExecutor().execute(() -> onRemoveCallDiagnostics(callDiagnostics));
}
}
/**
* Handles an incoming device to device message received from Telecom. Notifies the
- * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}.
+ * {@link CallDiagnostics} via {@link CallDiagnostics#onReceiveDeviceToDeviceMessage(int, int)}.
* @param callId
* @param message
* @param value
*/
private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) {
Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value);
- DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
- diagnosticCall.onReceiveDeviceToDeviceMessage(message, value);
+ CallDiagnostics callDiagnostics;
+ synchronized (mLock) {
+ callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
+ }
+ if (callDiagnostics != null) {
+ getExecutor().execute(
+ () -> callDiagnostics.onReceiveDeviceToDeviceMessage(message, value));
+ }
+ }
+
+ /**
+ * Handles a request from the Telecom framework to get a disconnect message from the
+ * {@link CallDiagnosticService}.
+ * @param callId The ID of the call.
+ * @param disconnectCause The telecom disconnect cause.
+ */
+ private void handleCallDisconnected(@NonNull String callId,
+ @NonNull DisconnectCause disconnectCause) {
+ Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause);
+ CallDiagnostics callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
+ CharSequence message;
+ if (disconnectCause.getImsReasonInfo() != null) {
+ message = callDiagnostics.onCallDisconnected(disconnectCause.getImsReasonInfo());
+ } else {
+ message = callDiagnostics.onCallDisconnected(
+ disconnectCause.getTelephonyDisconnectCause(),
+ disconnectCause.getTelephonyPreciseDisconnectCause());
+ }
+ try {
+ mAdapter.overrideDisconnectMessage(callId, message);
+ } catch (RemoteException e) {
+ Log.w(this, "handleCallDisconnected: call=%s; cause=%s; %s",
+ callId, disconnectCause, e);
+ }
}
/**
@@ -265,19 +371,19 @@ public abstract class CallDiagnosticService extends Service {
private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport
qualityReport) {
Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport);
- onBluetoothCallQualityReportReceived(qualityReport);
+ getExecutor().execute(() -> onBluetoothCallQualityReportReceived(qualityReport));
}
/**
- * Handles a request from a {@link DiagnosticCall} to send a device to device message (received
- * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}.
- * @param diagnosticCall
+ * Handles a request from a {@link CallDiagnostics} to send a device to device message (received
+ * via {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}.
+ * @param callDiagnostics
* @param message
* @param value
*/
- private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall,
+ private void handleSendDeviceToDeviceMessage(@NonNull CallDiagnostics callDiagnostics,
int message, int value) {
- String callId = diagnosticCall.getCallId();
+ String callId = callDiagnostics.getCallId();
try {
mAdapter.sendDeviceToDeviceMessage(callId, message, value);
Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message,
@@ -289,15 +395,15 @@ public abstract class CallDiagnosticService extends Service {
}
/**
- * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message.
- * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}.
- * @param diagnosticCall
+ * Handles a request from a {@link CallDiagnostics} to display an in-call diagnostic message.
+ * Originates from {@link CallDiagnostics#displayDiagnosticMessage(int, CharSequence)}.
+ * @param callDiagnostics
* @param messageId
* @param message
*/
- private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ private void handleDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId,
CharSequence message) {
- String callId = diagnosticCall.getCallId();
+ String callId = callDiagnostics.getCallId();
try {
mAdapter.displayDiagnosticMessage(callId, messageId, message);
Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId,
@@ -309,14 +415,14 @@ public abstract class CallDiagnosticService extends Service {
}
/**
- * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic
+ * Handles a request from a {@link CallDiagnostics} to clear a previously shown diagnostic
* message.
- * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}.
- * @param diagnosticCall
+ * Originates from {@link CallDiagnostics#clearDiagnosticMessage(int)}.
+ * @param callDiagnostics
* @param messageId
*/
- private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
- String callId = diagnosticCall.getCallId();
+ private void handleClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId) {
+ String callId = callDiagnostics.getCallId();
try {
mAdapter.clearDiagnosticMessage(callId, messageId);
Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId);
diff --git a/telecomm/java/android/telecom/CallDiagnostics.java b/telecomm/java/android/telecom/CallDiagnostics.java
new file mode 100644
index 000000000000..3356431f17b1
--- /dev/null
+++ b/telecomm/java/android/telecom/CallDiagnostics.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.telephony.Annotation;
+import android.telephony.CallQuality;
+import android.telephony.ims.ImsReasonInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link CallDiagnostics} provides a way for a {@link CallDiagnosticService} to receive diagnostic
+ * information about a mobile call on the device. A {@link CallDiagnostics} instance is similar to
+ * a {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic
+ * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService}
+ * creates a {@link CallDiagnostics} for each {@link Call} on the device. This means that for each
+ * in progress call on the device, the {@link CallDiagnosticService} will create an instance of
+ * {@link CallDiagnostics}.
+ * <p>
+ * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the
+ * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable
+ * information about conditions impacting their call and corrective actions. For example, if the
+ * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform
+ * the user that the call may soon drop and that they can try using a different calling method
+ * (e.g. VOIP or WIFI).
+ * <h2>Threading Model</h2>
+ * All incoming IPC from Telecom in this class will use the same {@link Executor} as the
+ * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more
+ * information.
+ * @hide
+ */
+@SystemApi
+public abstract class CallDiagnostics {
+
+ /**
+ * @hide
+ */
+ public interface Listener {
+ /**
+ * Used to inform the {@link CallDiagnosticService} of a request to send a D2d message
+ * @param callDiagnostics the call the message is from.
+ * @param message the message type
+ * @param value the message value
+ */
+ void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, int message, int value);
+
+ /**
+ * Used to inform the {@link CallDiagnosticService} of a request to display a diagnostic
+ * message.
+ * @param callDiagnostics the call the message pertains to.
+ * @param messageId an identifier for the message.
+ * @param message the message to display.
+ */
+ void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId,
+ CharSequence message);
+
+ /**
+ * Used to inform the {@link CallDiagnosticService} that a previously shown message is no
+ * longer pertinent.
+ * @param callDiagnostics the call the message pertains to.
+ * @param messageId the ID of the previously posted message.
+ */
+ void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId);
+ }
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
+ * used for the current call. The call network type communicated here is an intentional
+ * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which
+ * removes some of the resolution inherent in those values; the
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is
+ * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for
+ * efficiency of transport. For a discussion on the necessity of this simplification, see
+ * {@link #sendDeviceToDeviceMessage(int, int)}.
+ * <p>
+ * Valid values are below:
+ * <UL>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
+ * used for the current call.
+ * <p>
+ * The audio codec communicated here is an intentional simplification of the
+ * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common
+ * variants of these audio codecs. Other variants of these codecs are reported as the next
+ * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec
+ * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}.
+ * For a discussion on the necessity of this simplification, see
+ * {@link #sendDeviceToDeviceMessage(int, int)}.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of
+ * the device. Will typically mirror battery state reported via intents such as
+ * {@link android.content.Intent#ACTION_BATTERY_LOW}.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #BATTERY_STATE_LOW}</LI>
+ * <LI>{@link #BATTERY_STATE_GOOD}</LI>
+ * <LI>{@link #BATTERY_STATE_CHARGING}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network
+ * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal
+ * poor coverage if the network coverage reaches a level where there is a high probability of
+ * the call dropping as a result.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #COVERAGE_POOR}</LI>
+ * <LI>{@link #COVERAGE_GOOD}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MESSAGE_", value = {
+ MESSAGE_CALL_NETWORK_TYPE,
+ MESSAGE_CALL_AUDIO_CODEC,
+ MESSAGE_DEVICE_BATTERY_STATE,
+ MESSAGE_DEVICE_NETWORK_COVERAGE
+ })
+ public @interface MessageType {}
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
+ */
+ public static final int BATTERY_STATE_LOW = 1;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low.
+ */
+ public static final int BATTERY_STATE_GOOD = 2;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging.
+ */
+ public static final int BATTERY_STATE_CHARGING = 3;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor.
+ */
+ public static final int COVERAGE_POOR = 1;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good.
+ */
+ public static final int COVERAGE_GOOD = 2;
+
+ private Listener mListener;
+ private String mCallId;
+
+ /**
+ * @hide
+ */
+ public void setListener(@NonNull Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Sets the call ID for this {@link CallDiagnostics}.
+ * @param callId
+ * @hide
+ */
+ public void setCallId(@NonNull String callId) {
+ mCallId = callId;
+ }
+
+ /**
+ * @return the Telecom call ID for this {@link CallDiagnostics}.
+ * @hide
+ */
+ public @NonNull String getCallId() {
+ return mCallId;
+ }
+
+ /**
+ * Telecom calls this method when the details of a call changes.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
+ */
+ public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
+
+ /**
+ * The {@link CallDiagnosticService} implements this method to handle messages received via
+ * device to device communication.
+ * <p>
+ * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device
+ * communication.
+ * <p>
+ * The underlying device to device communication protocol assumes that where there the two
+ * devices communicating are using a different version of the protocol, messages the recipient
+ * are not aware of are silently discarded. This means an older client talking to a new client
+ * will not receive newer messages and values sent by the new client.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
+ */
+ public abstract void onReceiveDeviceToDeviceMessage(
+ @MessageType int message,
+ int value);
+
+ /**
+ * Sends a device to device message to the device on the other end of this call.
+ * <p>
+ * Device to device communication is an Android platform feature which supports low bandwidth
+ * communication between Android devices while they are in a call. The device to device
+ * communication leverages DTMF tones or RTP header extensions to pass messages. The
+ * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the
+ * nature of the message are informational only and are used only to convey basic state
+ * information between devices.
+ * <p>
+ * Device to device messages are intentional simplifications of more rich indicators in the
+ * platform due to the extreme bandwidth constraints inherent with underlying device to device
+ * communication transports used by the telephony framework. Device to device communication is
+ * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
+ * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension
+ * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for
+ * a message. Signalling requirements for DTMF digits place even more significant limitations
+ * on the amount of information which can be communicated during a call, offering only a few
+ * bits of potential information per message. The messages and values are constrained in order
+ * to meet the limited bandwidth inherent with DTMF signalling.
+ * <p>
+ * Allowed message types are:
+ * <ul>
+ * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI>
+ * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI>
+ * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI>
+ * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI>
+ * </ul>
+ * @param message The message type to send.
+ * @param value The message value corresponding to the type.
+ */
+ public final void sendDeviceToDeviceMessage(int message, int value) {
+ if (mListener != null) {
+ mListener.onSendDeviceToDeviceMessage(this, message, value);
+ }
+ }
+
+ /**
+ * Telecom calls this method when a GSM or CDMA call disconnects.
+ * The CallDiagnosticService can return a human readable disconnect message which will be passed
+ * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically
+ * shows this message at the termination of the call. If {@code null} is returned, the
+ * disconnect message generated by the telephony stack will be shown instead.
+ * <p>
+ * @param disconnectCause the disconnect cause for the call.
+ * @param preciseDisconnectCause the precise disconnect cause for the call.
+ * @return the disconnect message to use in place of the default Telephony message, or
+ * {@code null} if the default message will not be overridden.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
+ */
+ // TODO: Wire in Telephony support for this.
+ public abstract @Nullable CharSequence onCallDisconnected(
+ @Annotation.DisconnectCauses int disconnectCause,
+ @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+
+ /**
+ * Telecom calls this method when an IMS call disconnects and Telephony has already
+ * provided the disconnect reason info and disconnect message for the call. The
+ * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and
+ * combine it with other call diagnostic information it is aware of to override the disconnect
+ * call message if desired.
+ *
+ * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
+ * @return A user-readable call disconnect message to use in place of the platform-generated
+ * disconnect message, or {@code null} if the disconnect message should not be overridden.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
+ */
+ // TODO: Wire in Telephony support for this.
+ public abstract @Nullable CharSequence onCallDisconnected(
+ @NonNull ImsReasonInfo disconnectReason);
+
+ /**
+ * Telecom calls this method when a {@link CallQuality} report is received from the telephony
+ * stack for a call.
+ * @param callQuality The call quality report for this call.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
+ */
+ public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
+
+ /**
+ * Signals the active default dialer app to display a call diagnostic message. This can be
+ * used to report problems encountered during the span of a call.
+ * <p>
+ * The {@link CallDiagnosticService} provides a unique client-specific identifier used to
+ * identify the specific diagnostic message type.
+ * <p>
+ * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the
+ * diagnostic condition has cleared.
+ * @param messageId the unique message identifier.
+ * @param message a human-readable, localized message to be shown to the user indicating a
+ * call issue which has occurred, along with potential mitigating actions.
+ */
+ public final void displayDiagnosticMessage(int messageId, @NonNull
+ CharSequence message) {
+ if (mListener != null) {
+ mListener.onDisplayDiagnosticMessage(this, messageId, message);
+ }
+ }
+
+ /**
+ * Signals to the active default dialer that the diagnostic message previously signalled using
+ * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no
+ * longer applicable (e.g. service has improved, for example.
+ * @param messageId the message identifier for a message previously shown via
+ * {@link #displayDiagnosticMessage(int, CharSequence)}.
+ */
+ public final void clearDiagnosticMessage(int messageId) {
+ if (mListener != null) {
+ mListener.onClearDiagnosticMessage(this, messageId);
+ }
+ }
+
+ /**
+ * Called by the {@link CallDiagnosticService} to update the call details for this
+ * {@link CallDiagnostics} based on an update received from Telecom.
+ * @param newDetails the new call details.
+ * @hide
+ */
+ public void handleCallUpdated(@NonNull Call.Details newDetails) {
+ onCallDetailsChanged(newDetails);
+ }
+}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index dc2fb948fdbe..f84dd7b0bb17 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -338,7 +338,7 @@ public abstract class Conference extends Conferenceable {
*
* @param videoState The video state in which to answer the connection.
*/
- public void onAnswer(int videoState) {}
+ public void onAnswer(@VideoProfile.VideoState int videoState) {}
/**
* Notifies this Conference, which is in {@code STATE_RINGING}, of
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 335857af8883..6dab6df22cf9 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -947,7 +947,7 @@ public abstract class Connection extends Conferenceable {
* {@link CallDiagnosticService} implementation which is active.
* <p>
* Likewise, if a {@link CallDiagnosticService} sends a message using
- * {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony
+ * {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony
* via {@link Connection#onCallEvent(String, Bundle)}. The telephony stack will relay the
* message to the other device.
* @hide
@@ -960,7 +960,7 @@ public abstract class Connection extends Conferenceable {
* Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
* message type.
*
- * See {@link DiagnosticCall} for more information.
+ * See {@link CallDiagnostics} for more information.
* @hide
*/
@SystemApi
@@ -971,7 +971,7 @@ public abstract class Connection extends Conferenceable {
* Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
* message value.
* <p>
- * See {@link DiagnosticCall} for more information.
+ * See {@link CallDiagnostics} for more information.
* @hide
*/
@SystemApi
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index c189b19c71af..d8bd6a576fad 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
@@ -1919,6 +1920,7 @@ public abstract class ConnectionService extends Service {
/** {@inheritDoc} */
@Override
public final IBinder onBind(Intent intent) {
+ onBindClient(intent);
return mBinder;
}
@@ -1929,6 +1931,13 @@ public abstract class ConnectionService extends Service {
return super.onUnbind(intent);
}
+ /**
+ * Used for testing to let the test suite know when the connection service has been bound.
+ * @hide
+ */
+ @TestApi
+ public void onBindClient(@Nullable Intent intent) {
+ }
/**
* This can be used by telecom to either create a new outgoing conference call or attach
@@ -2585,9 +2594,9 @@ public abstract class ConnectionService extends Service {
* @return The {@code Connection} object to satisfy this call, or {@code null} to
* not handle the call.
*/
- public final RemoteConnection createRemoteIncomingConnection(
- PhoneAccountHandle connectionManagerPhoneAccount,
- ConnectionRequest request) {
+ public final @Nullable RemoteConnection createRemoteIncomingConnection(
+ @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull ConnectionRequest request) {
return mRemoteConnectionManager.createRemoteConnection(
connectionManagerPhoneAccount, request, true);
}
@@ -2604,9 +2613,9 @@ public abstract class ConnectionService extends Service {
* @return The {@code Connection} object to satisfy this call, or {@code null} to
* not handle the call.
*/
- public final RemoteConnection createRemoteOutgoingConnection(
- PhoneAccountHandle connectionManagerPhoneAccount,
- ConnectionRequest request) {
+ public final @Nullable RemoteConnection createRemoteOutgoingConnection(
+ @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull ConnectionRequest request) {
return mRemoteConnectionManager.createRemoteConnection(
connectionManagerPhoneAccount, request, false);
}
@@ -2846,12 +2855,14 @@ public abstract class ConnectionService extends Service {
* @param connectionManagerPhoneAccount See description at
* {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
* @param request Details about the incoming conference call.
- * @return The {@code Conference} object to satisfy this call, or {@code null} to
- * not handle the call.
+ * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+ * failed, the return value will be a result of an invocation of
+ * {@link Connection#createFailedConnection(DisconnectCause)}.
+ * Return {@code null} if the {@link ConnectionService} cannot handle the call.
*/
public @Nullable Conference onCreateIncomingConference(
- @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
- @Nullable ConnectionRequest request) {
+ @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull ConnectionRequest request) {
return null;
}
@@ -2954,8 +2965,8 @@ public abstract class ConnectionService extends Service {
* @param request The outgoing connection request.
*/
public void onCreateOutgoingConferenceFailed(
- @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
- @Nullable ConnectionRequest request) {
+ @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull ConnectionRequest request) {
}
@@ -3019,12 +3030,14 @@ public abstract class ConnectionService extends Service {
* a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
* making the connection.
* @param request Details about the outgoing call.
- * @return The {@code Conference} object to satisfy this call, or the result of an invocation
- * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+ * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+ * failed, the return value will be a result of an invocation of
+ * {@link Connection#createFailedConnection(DisconnectCause)}.
+ * Return {@code null} if the {@link ConnectionService} cannot handle the call.
*/
public @Nullable Conference onCreateOutgoingConference(
- @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
- @Nullable ConnectionRequest request) {
+ @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull ConnectionRequest request) {
return null;
}
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java
index a4952899eb46..a6b7258052a4 100644
--- a/telecomm/java/android/telecom/DiagnosticCall.java
+++ b/telecomm/java/android/telecom/DiagnosticCall.java
@@ -16,366 +16,12 @@
package android.telecom;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.telephony.Annotation;
-import android.telephony.CallQuality;
-import android.telephony.ims.ImsReasonInfo;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
/**
- * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic
- * information about a mobile call on the device. The {@link CallDiagnosticService} can generate
- * mid-call diagnostic messages using the {@link #displayDiagnosticMessage(int, CharSequence)} API
- * which provides the user with valuable information about conditions impacting their call and
- * corrective actions. For example, if the {@link CallDiagnosticService} determines that conditions
- * on the call are degrading, it can inform the user that the call may soon drop and that they
- * can try using a different calling method (e.g. VOIP or WIFI).
+ * @deprecated use {@link CallDiagnostics} instead.
* @hide
*/
@SystemApi
-public abstract class DiagnosticCall {
-
- /**
- * @hide
- */
- public interface Listener {
- void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value);
- void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
- CharSequence message);
- void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId);
- }
-
- /**
- * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
- * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
- * used for the current call. Based loosely on the
- * {@link android.telephony.TelephonyManager#getNetworkType(int)} for the call, provides a
- * high level summary of the call radio access type.
- * <p>
- * Valid values:
- * <UL>
- * <LI>{@link #NETWORK_TYPE_LTE}</LI>
- * <LI>{@link #NETWORK_TYPE_IWLAN}</LI>
- * <LI>{@link #NETWORK_TYPE_NR}</LI>
- * </UL>
- */
- public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
-
- /**
- * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
- * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
- * used for the current call. Based loosely on the {@link Connection#EXTRA_AUDIO_CODEC} for a
- * call.
- * <p>
- * Valid values:
- * <UL>
- * <LI>{@link #AUDIO_CODEC_EVS}</LI>
- * <LI>{@link #AUDIO_CODEC_AMR_WB}</LI>
- * <LI>{@link #AUDIO_CODEC_AMR_NB}</LI>
- * </UL>
- */
- public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
-
- /**
- * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
- * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of
- * the device. Will typically mirror battery state reported via intents such as
- * {@link android.content.Intent#ACTION_BATTERY_LOW}.
- * <p>
- * Valid values:
- * <UL>
- * <LI>{@link #BATTERY_STATE_LOW}</LI>
- * <LI>{@link #BATTERY_STATE_GOOD}</LI>
- * <LI>{@link #BATTERY_STATE_CHARGING}</LI>
- * </UL>
- */
- public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
-
- /**
- * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
- * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network
- * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal
- * poor coverage if the network coverage reaches a level where there is a high probability of
- * the call dropping as a result.
- * <p>
- * Valid values:
- * <UL>
- * <LI>{@link #COVERAGE_POOR}</LI>
- * <LI>{@link #COVERAGE_GOOD}</LI>
- * </UL>
- */
- public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
-
- /**@hide*/
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "MESSAGE_", value = {
- MESSAGE_CALL_NETWORK_TYPE,
- MESSAGE_CALL_AUDIO_CODEC,
- MESSAGE_DEVICE_BATTERY_STATE,
- MESSAGE_DEVICE_NETWORK_COVERAGE
- })
- public @interface MessageType {}
-
- /**
- * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate an LTE network is being used for the
- * call.
- */
- public static final int NETWORK_TYPE_LTE = 1;
-
- /**
- * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate WIFI calling is in use for the call.
- */
- public static final int NETWORK_TYPE_IWLAN = 2;
-
- /**
- * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate a 5G NR (new radio) network is in
- * used for the call.
- */
- public static final int NETWORK_TYPE_NR = 3;
-
- /**
- * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the
- * Enhanced Voice Services (EVS) codec for the call.
- */
- public static final int AUDIO_CODEC_EVS = 1;
-
- /**
- * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
- * (adaptive multi-rate) WB (wide band) audio codec.
- */
- public static final int AUDIO_CODEC_AMR_WB = 2;
-
- /**
- * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
- * (adaptive multi-rate) NB (narrow band) audio codec.
- */
- public static final int AUDIO_CODEC_AMR_NB = 3;
-
- /**
- * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
- */
- public static final int BATTERY_STATE_LOW = 1;
-
- /**
- * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low.
- */
- public static final int BATTERY_STATE_GOOD = 2;
-
- /**
- * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging.
- */
- public static final int BATTERY_STATE_CHARGING = 3;
-
- /**
- * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor.
- */
- public static final int COVERAGE_POOR = 1;
-
- /**
- * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good.
- */
- public static final int COVERAGE_GOOD = 2;
-
- private Listener mListener;
- private String mCallId;
- private Call.Details mCallDetails;
-
- /**
- * @hide
- */
- public void setListener(@NonNull Listener listener) {
- mListener = listener;
- }
-
- /**
- * Sets the call ID for this {@link DiagnosticCall}.
- * @param callId
- * @hide
- */
- public void setCallId(@NonNull String callId) {
- mCallId = callId;
- }
-
- /**
- * @return the Telecom call ID for this {@link DiagnosticCall}.
- * @hide
- */
- public @NonNull String getCallId() {
- return mCallId;
- }
-
- /**
- * Returns the latest {@link Call.Details} associated with this {@link DiagnosticCall} as
- * reported by {@link #onCallDetailsChanged(Call.Details)}.
- * @return The latest {@link Call.Details}.
- */
- public @NonNull Call.Details getCallDetails() {
- return mCallDetails;
- }
-
- /**
- * Telecom calls this method when the details of a call changes.
- */
- public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
-
- /**
- * The {@link CallDiagnosticService} implements this method to handle messages received via
- * device to device communication.
- * <p>
- * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device
- * communication.
- * <p>
- * The underlying device to device communication protocol assumes that where there the two
- * devices communicating are using a different version of the protocol, messages the recipient
- * are not aware of are silently discarded. This means an older client talking to a new client
- * will not receive newer messages and values sent by the new client.
- */
- public abstract void onReceiveDeviceToDeviceMessage(
- @MessageType int message,
- int value);
-
- /**
- * Sends a device to device message to the device on the other end of this call.
- * <p>
- * Device to device communication is an Android platform feature which supports low bandwidth
- * communication between Android devices while they are in a call. The device to device
- * communication leverages DTMF tones or RTP header extensions to pass messages. The
- * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the
- * nature of the message are informational only and are used only to convey basic state
- * information between devices.
- * <p>
- * Device to device messages are intentional simplifications of more rich indicators in the
- * platform due to the extreme bandwidth constraints inherent with underlying device to device
- * communication transports used by the telephony framework. Device to device communication is
- * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
- * for a call, or using the DTMF digits A-D as a communication pathway. Signalling requirements
- * for DTMF digits place a significant limitation on the amount of information which can be
- * communicated during a call.
- * <p>
- * Allowed message types and values are:
- * <ul>
- * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}
- * <ul>
- * <li>{@link #NETWORK_TYPE_LTE}</li>
- * <li>{@link #NETWORK_TYPE_IWLAN}</li>
- * <li>{@link #NETWORK_TYPE_NR}</li>
- * </ul>
- * </li>
- * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}
- * <ul>
- * <li>{@link #AUDIO_CODEC_EVS}</li>
- * <li>{@link #AUDIO_CODEC_AMR_WB}</li>
- * <li>{@link #AUDIO_CODEC_AMR_NB}</li>
- * </ul>
- * </li>
- * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}
- * <ul>
- * <li>{@link #BATTERY_STATE_LOW}</li>
- * <li>{@link #BATTERY_STATE_GOOD}</li>
- * <li>{@link #BATTERY_STATE_CHARGING}</li>
- * </ul>
- * </li>
- * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}
- * <ul>
- * <li>{@link #COVERAGE_POOR}</li>
- * <li>{@link #COVERAGE_GOOD}</li>
- * </ul>
- * </li>
- * </ul>
- * @param message The message type to send.
- * @param value The message value corresponding to the type.
- */
- public final void sendDeviceToDeviceMessage(int message, int value) {
- if (mListener != null) {
- mListener.onSendDeviceToDeviceMessage(this, message, value);
- }
- }
-
- /**
- * Telecom calls this method when a GSM or CDMA call disconnects.
- * The CallDiagnosticService can return a human readable disconnect message which will be passed
- * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically
- * shows this message at the termination of the call. If {@code null} is returned, the
- * disconnect message generated by the telephony stack will be shown instead.
- * <p>
- * @param disconnectCause the disconnect cause for the call.
- * @param preciseDisconnectCause the precise disconnect cause for the call.
- * @return the disconnect message to use in place of the default Telephony message, or
- * {@code null} if the default message will not be overridden.
- */
- // TODO: Wire in Telephony support for this.
- public abstract @Nullable CharSequence onCallDisconnected(
- @Annotation.DisconnectCauses int disconnectCause,
- @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
-
- /**
- * Telecom calls this method when an IMS call disconnects and Telephony has already
- * provided the disconnect reason info and disconnect message for the call. The
- * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and
- * combine it with other call diagnostic information it is aware of to override the disconnect
- * call message if desired.
- *
- * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
- * @return A user-readable call disconnect message to use in place of the platform-generated
- * disconnect message, or {@code null} if the disconnect message should not be overridden.
- */
- // TODO: Wire in Telephony support for this.
- public abstract @Nullable CharSequence onCallDisconnected(
- @NonNull ImsReasonInfo disconnectReason);
-
- /**
- * Telecom calls this method when a {@link CallQuality} report is received from the telephony
- * stack for a call.
- * @param callQuality The call quality report for this call.
- */
- public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
-
- /**
- * Signals the active default dialer app to display a call diagnostic message. This can be
- * used to report problems encountered during the span of a call.
- * <p>
- * The {@link CallDiagnosticService} provides a unique client-specific identifier used to
- * identify the specific diagnostic message type.
- * <p>
- * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the
- * diagnostic condition has cleared.
- * @param messageId the unique message identifier.
- * @param message a human-readable, localized message to be shown to the user indicating a
- * call issue which has occurred, along with potential mitigating actions.
- */
- public final void displayDiagnosticMessage(int messageId, @NonNull
- CharSequence message) {
- if (mListener != null) {
- mListener.onDisplayDiagnosticMessage(this, messageId, message);
- }
- }
-
- /**
- * Signals to the active default dialer that the diagnostic message previously signalled using
- * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no
- * longer applicable (e.g. service has improved, for example.
- * @param messageId the message identifier for a message previously shown via
- * {@link #displayDiagnosticMessage(int, CharSequence)}.
- */
- public final void clearDiagnosticMessage(int messageId) {
- if (mListener != null) {
- mListener.onClearDiagnosticMessage(this, messageId);
- }
- }
-
- /**
- * Called by the {@link CallDiagnosticService} to update the call details for this
- * {@link DiagnosticCall} based on an update received from Telecom.
- * @param newDetails the new call details.
- * @hide
- */
- public void handleCallUpdated(@NonNull Call.Details newDetails) {
- mCallDetails = newDetails;
- onCallDetailsChanged(newDetails);
- }
+public abstract class DiagnosticCall extends CallDiagnostics {
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1472a4ac27bc..ed7b79f62753 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,13 @@
package android.telecom;
+import android.annotation.Nullable;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation;
+import android.telephony.PreciseDisconnectCause;
+import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
import java.util.Objects;
@@ -112,6 +116,9 @@ public final class DisconnectCause implements Parcelable {
private CharSequence mDisconnectDescription;
private String mDisconnectReason;
private int mToneToPlay;
+ private int mTelephonyDisconnectCause;
+ private int mTelephonyPreciseDisconnectCause;
+ private ImsReasonInfo mImsReasonInfo;
/**
* Creates a new DisconnectCause.
@@ -155,11 +162,36 @@ public final class DisconnectCause implements Parcelable {
*/
public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
int toneToPlay) {
+ this(code, label, description, reason, toneToPlay,
+ android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+ PreciseDisconnectCause.ERROR_UNSPECIFIED,
+ null /* imsReasonInfo */);
+ }
+
+ /**
+ * Creates a new DisconnectCause instance.
+ * @param code The code for the disconnect cause.
+ * @param label The localized label to show to the user to explain the disconnect.
+ * @param description The localized description to show to the user to explain the disconnect.
+ * @param reason The reason for the disconnect.
+ * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
+ * @param telephonyDisconnectCause The Telephony disconnect cause.
+ * @param telephonyPreciseDisconnectCause The Telephony precise disconnect cause.
+ * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
+ * @hide
+ */
+ public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+ int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
+ @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
+ @Nullable ImsReasonInfo imsReasonInfo) {
mDisconnectCode = code;
mDisconnectLabel = label;
mDisconnectDescription = description;
mDisconnectReason = reason;
mToneToPlay = toneToPlay;
+ mTelephonyDisconnectCause = telephonyDisconnectCause;
+ mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause;
+ mImsReasonInfo = imsReasonInfo;
}
/**
@@ -209,6 +241,33 @@ public final class DisconnectCause implements Parcelable {
}
/**
+ * Returns the telephony {@link android.telephony.DisconnectCause} for the call.
+ * @return The disconnect cause.
+ * @hide
+ */
+ public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() {
+ return mTelephonyDisconnectCause;
+ }
+
+ /**
+ * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call.
+ * @return The precise disconnect cause.
+ * @hide
+ */
+ public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() {
+ return mTelephonyPreciseDisconnectCause;
+ }
+
+ /**
+ * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection.
+ * @return The {@link ImsReasonInfo} or {@code null} if not known.
+ * @hide
+ */
+ public @Nullable ImsReasonInfo getImsReasonInfo() {
+ return mImsReasonInfo;
+ }
+
+ /**
* Returns the tone to play when disconnected.
*
* @return the tone as defined in {@link ToneGenerator} to play when disconnected.
@@ -217,7 +276,8 @@ public final class DisconnectCause implements Parcelable {
return mToneToPlay;
}
- public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
+ public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR
+ = new Creator<DisconnectCause>() {
@Override
public DisconnectCause createFromParcel(Parcel source) {
int code = source.readInt();
@@ -225,7 +285,11 @@ public final class DisconnectCause implements Parcelable {
CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
String reason = source.readString();
int tone = source.readInt();
- return new DisconnectCause(code, label, description, reason, tone);
+ int telephonyDisconnectCause = source.readInt();
+ int telephonyPreciseDisconnectCause = source.readInt();
+ ImsReasonInfo imsReasonInfo = source.readParcelable(null);
+ return new DisconnectCause(code, label, description, reason, tone,
+ telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo);
}
@Override
@@ -241,6 +305,9 @@ public final class DisconnectCause implements Parcelable {
TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
destination.writeString(mDisconnectReason);
destination.writeInt(mToneToPlay);
+ destination.writeInt(mTelephonyDisconnectCause);
+ destination.writeInt(mTelephonyPreciseDisconnectCause);
+ destination.writeParcelable(mImsReasonInfo, 0);
}
@Override
@@ -254,7 +321,10 @@ public final class DisconnectCause implements Parcelable {
+ Objects.hashCode(mDisconnectLabel)
+ Objects.hashCode(mDisconnectDescription)
+ Objects.hashCode(mDisconnectReason)
- + Objects.hashCode(mToneToPlay);
+ + Objects.hashCode(mToneToPlay)
+ + Objects.hashCode(mTelephonyDisconnectCause)
+ + Objects.hashCode(mTelephonyPreciseDisconnectCause)
+ + Objects.hashCode(mImsReasonInfo);
}
@Override
@@ -265,7 +335,11 @@ public final class DisconnectCause implements Parcelable {
&& Objects.equals(mDisconnectLabel, d.getLabel())
&& Objects.equals(mDisconnectDescription, d.getDescription())
&& Objects.equals(mDisconnectReason, d.getReason())
- && Objects.equals(mToneToPlay, d.getTone());
+ && Objects.equals(mToneToPlay, d.getTone())
+ && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause())
+ && Objects.equals(mTelephonyPreciseDisconnectCause,
+ d.getTelephonyPreciseDisconnectCause())
+ && Objects.equals(mImsReasonInfo, d.getImsReasonInfo());
}
return false;
}
@@ -325,6 +399,11 @@ public final class DisconnectCause implements Parcelable {
+ " Label: (" + label + ")"
+ " Description: (" + description + ")"
+ " Reason: (" + reason + ")"
- + " Tone: (" + mToneToPlay + ") ]";
+ + " Tone: (" + mToneToPlay + ") "
+ + " TelephonyCause: " + mTelephonyDisconnectCause + "/"
+ + mTelephonyPreciseDisconnectCause
+ + " ImsReasonInfo: "
+ + mImsReasonInfo
+ + "]";
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 17749e8b0a8f..1677c8c1f08e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,8 +314,8 @@ public class TelecomManager {
public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
/**
- * A URI representing the picture that was downloaded when a call is received or uploaded
- * when a call is placed.
+ * A {@link Uri} representing the picture that was downloaded when a call is received or
+ * uploaded when a call is placed.
*
* This is a content URI within the call log provider which can be used to open a file
* descriptor. This could be set a short time after a call is added to the Dialer app if the
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
index 65b4d19b3d9b..fc9879aaf0a8 100644
--- a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
@@ -18,6 +18,7 @@ package com.android.internal.telecom;
import android.telecom.BluetoothCallQualityReport;
import android.telecom.CallAudioState;
+import android.telecom.DisconnectCause;
import android.telecom.ParcelableCall;
import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
@@ -34,4 +35,5 @@ oneway interface ICallDiagnosticService {
void removeDiagnosticCall(in String callId);
void receiveDeviceToDeviceMessage(in String callId, int message, int value);
void receiveBluetoothCallQualityReport(in BluetoothCallQualityReport qualityReport);
+ void notifyCallDisconnected(in String callId, in DisconnectCause disconnectCause);
}
diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java
index b153fefce6e3..9e1dee0162b9 100644
--- a/telephony/java/android/telephony/CarrierBandwidth.java
+++ b/telephony/java/android/telephony/CarrierBandwidth.java
@@ -101,7 +101,7 @@ public final class CarrierBandwidth implements Parcelable {
}
/**
- * Retrieves the upstream bandwidth for the primary network in Kbps. This always only refers to
+ * Retrieves the upstream bandwidth for the primary network in kbps. This always only refers to
* the estimated first hop transport bandwidth.
* This will be {@link #INVALID} if the network is not connected
*
@@ -112,7 +112,7 @@ public final class CarrierBandwidth implements Parcelable {
}
/**
- * Retrieves the downstream bandwidth for the primary network in Kbps. This always only refers
+ * Retrieves the downstream bandwidth for the primary network in kbps. This always only refers
* to the estimated first hop transport bandwidth.
* This will be {@link #INVALID} if the network is not connected
*
@@ -123,7 +123,7 @@ public final class CarrierBandwidth implements Parcelable {
}
/**
- * Retrieves the upstream bandwidth for the secondary network in Kbps. This always only refers
+ * Retrieves the upstream bandwidth for the secondary network in kbps. This always only refers
* to the estimated first hop transport bandwidth.
* <p/>
* This will be {@link #INVALID} if either are the case:
@@ -143,7 +143,7 @@ public final class CarrierBandwidth implements Parcelable {
}
/**
- * Retrieves the downstream bandwidth for the secondary network in Kbps. This always only
+ * Retrieves the downstream bandwidth for the secondary network in kbps. This always only
* refers to the estimated first hop transport bandwidth.
* <p/>
* This will be {@link #INVALID} if either are the case:
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b52f49190679..74421a08b22d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -27,15 +27,17 @@ import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
+import android.net.ipsec.ike.SaProposal;
import android.os.Build;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.service.carrier.CarrierService;
import android.telecom.TelecomManager;
+import android.telephony.gba.TlsParams;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.ImsSsData;
-import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
@@ -1341,6 +1343,38 @@ public class CarrierConfigManager {
"support_ims_conference_event_package_on_peer_bool";
/**
+ * Indicates whether the carrier supports the use of RFC8285 compliant RTP header extensions for
+ * the purpose of device to device communication while in a call.
+ * <p>
+ * See also {@link #KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL}.
+ */
+ public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL =
+ "supports_device_to_device_communication_using_rtp_bool";
+
+ /**
+ * Indicates whether the carrier supports the negotiations of RFC8285 compliant RTP header
+ * extensions supported on a call during the Session Description Protocol (SDP). This option
+ * is only used when {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
+ * {@code true}.
+ * <p>
+ * When {@code true}, the RTP header extensions the platform uses for device to device
+ * communication will be offered to the remote end during the SDP negotiation process.
+ * When {@code false}, the RTP header extensions will not be negotiated during the SDP
+ * negotiation process and the platform will send RTP header extensions without prior
+ * negotiation if {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
+ * {@code true}.
+ */
+ public static final String KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL =
+ "supports_sdp_negotiation_of_d2d_rtp_header_extensions_bool";
+
+ /**
+ * Indicates whether the carrier supports the use of DTMF digits A-D for the purpose of device
+ * to device communication while in a call.
+ */
+ public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL =
+ "supports_device_to_device_communication_using_dtmf_bool";
+
+ /**
* Determines whether High Definition audio property is displayed in the dialer UI.
* If {@code false}, remove the HD audio property from the connection so that HD audio related
* UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -1537,15 +1571,13 @@ public class CarrierConfigManager {
"wfc_carrier_name_override_by_pnn_bool";
/**
- * Value for {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING} cnfig.
- * specifies SPN format of displaying carrier name only.
+ * Specifies SPN format of displaying carrier name only.
*
*/
public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0;
/**
- * Value for {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING} cnfig.
- * specifies SPN format of displaying carrier name along with "Cross-SIM calling".
+ * Specifies SPN format of displaying carrier name along with "Cross-SIM calling".
*/
public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1;
@@ -1554,8 +1586,8 @@ public class CarrierConfigManager {
*
* <p>Available options are:
* <ul>
- * <li> {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY}: %s</li>
- * <li> {#CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING}: %s Cross-SIM Calling</li>
+ * <li> {@link #CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY}: %s</li>
+ * <li> {@link #CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING}: %s Cross-SIM Calling</li>
* </ul>
* %s will be filled with carrier name
*/
@@ -2942,6 +2974,18 @@ public class CarrierConfigManager {
public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
/**
+ * Indicates if the carrier supports upgrading a call that was previously an RTT call to VT.
+ */
+ public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL =
+ "vt_upgrade_supported_for_downgraded_rtt_call";
+
+ /**
+ * Indicates if the carrier supports upgrading a call that was previously a VT call to RTT.
+ */
+ public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL =
+ "rtt_upgrade_supported_for_downgraded_vt_call";
+
+ /**
* Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
*/
public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
@@ -3666,6 +3710,70 @@ public class CarrierConfigManager {
*/
public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
+ /**
+ * Indicates that GBA_ME should be used for GBA authentication, as defined in 3GPP TS 33.220.
+ * @hide
+ */
+ @SystemApi
+ public static final int GBA_ME = 1;
+
+ /**
+ * Indicates that GBA_U should be used for GBA authentication, as defined in 3GPP TS 33.220.
+ * @hide
+ */
+ @SystemApi
+ public static final int GBA_U = 2;
+
+ /**
+ * Indicates that GBA_Digest should be used for GBA authentication, as defined
+ * in 3GPP TS 33.220.
+ * @hide
+ */
+ @SystemApi
+ public static final int GBA_DIGEST = 3;
+
+ /**
+ * An integer representing the GBA mode to use for requesting credentials
+ * via {@link TelephonyManager#bootstrapAuthenticationRequest}.
+ *
+ * One of {@link #GBA_ME}, {@link #GBA_U}, or {@link #GBA_DIGEST}.
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_GBA_MODE_INT = "gba_mode_int";
+
+ /**
+ * An integer representing the organization code to be used when building the
+ * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication.
+ *
+ * See the {@code ORG_} constants in {@link UaSecurityProtocolIdentifier}.
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT =
+ "gba_ua_security_organization_int";
+
+ /**
+ * An integer representing the security protocol to be used when building the
+ * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication.
+ *
+ * See the {@code UA_SECURITY_PROTOCOL_} constants in {@link UaSecurityProtocolIdentifier}.
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT =
+ "gba_ua_security_protocol_int";
+
+ /**
+ * An integer representing the cipher suite to be used when building the
+ * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication.
+ *
+ * See the {@code TLS_} constants in {@link android.telephony.gba.TlsParams}.
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT =
+ "gba_ua_tls_cipher_suite_int";
/**
* Configs used by ImsServiceEntitlement.
@@ -3969,6 +4077,22 @@ public class CarrierConfigManager {
"is_opportunistic_subscription_bool";
/**
+ * The flatten string {@link android.content.ComponentName componentName} of carrier
+ * provisioning app receiver.
+ *
+ * <p>
+ * The RadioInfo activity(*#*#INFO#*#*) will broadcast an intent to this receiver when the
+ * "Carrier Provisioning Info" or "Trigger Carrier Provisioning" button clicked.
+ *
+ * <p>
+ * e.g, com.google.android.carrierPackageName/.CarrierReceiverName
+ *
+ * @hide
+ */
+ public static final String KEY_CARRIER_PROVISIONING_APP_STRING =
+ "carrier_provisioning_app_string";
+
+ /**
* Configs used by the IMS stack.
*/
public static final class Ims {
@@ -4141,9 +4265,11 @@ public class CarrierConfigManager {
KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int";
/**
- * Supported DH groups for IKE negotiation. Possible values are {@link #DH_GROUP_NONE},
- * {@link #DH_GROUP_1024_BIT_MODP}, {@link #DH_GROUP_1536_BIT_MODP}, {@link
- * #DH_GROUP_2048_BIT_MODP}
+ * Supported DH groups for IKE negotiation. Possible values are:
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE},
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP},
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP},
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP}
*/
public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
KEY_PREFIX + "diffie_hellman_groups_int_array";
@@ -4179,23 +4305,29 @@ public class CarrierConfigManager {
/**
* List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child
- * session. Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link
- * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+ * session. Possible values are:
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
*/
public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
KEY_PREFIX + "child_session_aes_cbc_key_size_int_array";
/**
* List of supported key sizes for AES Counter (CTR) encryption mode of child session.
- * Possible values are {@link #KEY_LEN_UNUSED},
- * {@link #KEY_LEN_AES_128}, {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+ * Possible values are:
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
*/
public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
KEY_PREFIX + "child_session_aes_ctr_key_size_int_array";
/**
* List of supported encryption algorithms for child session. Possible values are
- * {@link #ENCRYPTION_ALGORITHM_AES_CBC}
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}
*/
public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
@@ -4216,8 +4348,11 @@ public class CarrierConfigManager {
/**
* List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE
- * session. Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link
- * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+ * session. Possible values:
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
*/
public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array";
@@ -4225,24 +4360,31 @@ public class CarrierConfigManager {
/**
* List of supported key sizes for AES Counter (CTR) encryption mode of IKE session.
- * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
- * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+ * Possible values -
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
*/
public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array";
/**
* List of supported encryption algorithms for IKE session. Possible values are
- * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_CTR}
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
*/
public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
/**
- * List of supported integrity algorithms for IKE session Possible values are {@link
- * #INTEGRITY_ALGORITHM_NONE}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA1_96}, {@link
- * #INTEGRITY_ALGORITHM_AES_XCBC_96}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, {@link
- * #INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
+ * List of supported integrity algorithms for IKE session. Possible values are
+ * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE},
+ * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96},
+ * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_AES_XCBC_96},
+ * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_256_128},
+ * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_384_192},
+ * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
*/
public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY =
KEY_PREFIX + "supported_integrity_algorithms_int_array";
@@ -4262,9 +4404,11 @@ public class CarrierConfigManager {
/**
* List of supported pseudo random function algorithms for IKE session. Possible values are
- * {@link #PSEUDORANDOM_FUNCTION_HMAC_SHA1}, {@link #PSEUDORANDOM_FUNCTION_AES128_XCBC},
- * {@link #PSEUDORANDOM_FUNCTION_SHA2_256}, {@link #PSEUDORANDOM_FUNCTION_SHA2_384},
- * {@link #PSEUDORANDOM_FUNCTION_SHA2_512}
+ * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_HMAC_SHA1},
+ * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_AES128_XCBC},
+ * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_256},
+ * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_384},
+ * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_512}
*/
public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY =
KEY_PREFIX + "supported_prf_algorithms_int_array";
@@ -4337,182 +4481,6 @@ public class CarrierConfigManager {
public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
/** @hide */
- @IntDef({KEY_LEN_UNUSED, KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256})
- public @interface EncrpytionKeyLengthType {}
-
- public static final int KEY_LEN_UNUSED = 0;
- /** AES Encryption/Ciphering Algorithm key length 128 bits. */
- public static final int KEY_LEN_AES_128 = 128;
- /** AES Encryption/Ciphering Algorithm key length 192 bits. */
- public static final int KEY_LEN_AES_192 = 192;
- /** AES Encryption/Ciphering Algorithm key length 256 bits. */
- public static final int KEY_LEN_AES_256 = 256;
-
- /** @hide */
- @IntDef({
- DH_GROUP_NONE,
- DH_GROUP_1024_BIT_MODP,
- DH_GROUP_1536_BIT_MODP,
- DH_GROUP_2048_BIT_MODP,
- DH_GROUP_3072_BIT_MODP,
- DH_GROUP_4096_BIT_MODP
- })
- public @interface DhGroup {}
-
- /** None Diffie-Hellman Group. */
- public static final int DH_GROUP_NONE = 0;
- /**
- * 1024-bit MODP Diffie-Hellman Group.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int DH_GROUP_1024_BIT_MODP = 2;
- /**
- * 1536-bit MODP Diffie-Hellman Group.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int DH_GROUP_1536_BIT_MODP = 5;
- /**
- * 2048-bit MODP Diffie-Hellman Group.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int DH_GROUP_2048_BIT_MODP = 14;
- /**
- * 3072-bit MODP Diffie-Hellman Group.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int DH_GROUP_3072_BIT_MODP = 15;
- /**
- * 4096-bit MODP Diffie-Hellman Group.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int DH_GROUP_4096_BIT_MODP = 16;
-
- /** @hide */
- @IntDef({ENCRYPTION_ALGORITHM_AES_CBC, ENCRYPTION_ALGORITHM_AES_CTR})
- public @interface EncryptionAlgorithm {}
-
- /**
- * AES-CBC Encryption/Ciphering Algorithm.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
-
- /**
- * AES-CTR Encryption/Ciphering Algorithm.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13;
-
- /** @hide */
- @IntDef({
- INTEGRITY_ALGORITHM_NONE,
- INTEGRITY_ALGORITHM_HMAC_SHA1_96,
- INTEGRITY_ALGORITHM_AES_XCBC_96,
- INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
- INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
- INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
- })
- public @interface IntegrityAlgorithm {}
-
- /** None Authentication/Integrity Algorithm. */
- public static final int INTEGRITY_ALGORITHM_NONE = 0;
- /**
- * HMAC-SHA1 Authentication/Integrity Algorithm.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2;
- /**
- * AES-XCBC-96 Authentication/Integrity Algorithm.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5;
- /**
- * HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12;
- /**
- * HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13;
- /**
- * HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14;
-
- /** @hide */
- @IntDef({
- PSEUDORANDOM_FUNCTION_HMAC_SHA1,
- PSEUDORANDOM_FUNCTION_AES128_XCBC,
- PSEUDORANDOM_FUNCTION_SHA2_256,
- PSEUDORANDOM_FUNCTION_SHA2_384,
- PSEUDORANDOM_FUNCTION_SHA2_512
- })
- public @interface PseudorandomFunction {}
-
- /**
- * HMAC-SHA1 Pseudorandom Function.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2;
- /**
- * AES128-XCBC Pseudorandom Function.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4;
- /**
- * HMAC-SHA2-256 Pseudorandom Function.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5;
- /**
- * HMAC-SHA2-384 Pseudorandom Function.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6;
- /**
- * HMAC-SHA2-384 Pseudorandom Function.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
- * Exchange Protocol Version 2 (IKEv2)</a>
- */
- public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7;
-
- /** @hide */
@IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
public @interface IkeIdType {}
@@ -4553,31 +4521,33 @@ public class CarrierConfigManager {
defaults.putIntArray(
KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY,
new int[] {
- DH_GROUP_1024_BIT_MODP, DH_GROUP_1536_BIT_MODP, DH_GROUP_2048_BIT_MODP
+ SaProposal.DH_GROUP_1024_BIT_MODP,
+ SaProposal.DH_GROUP_1536_BIT_MODP,
+ SaProposal.DH_GROUP_2048_BIT_MODP
});
defaults.putIntArray(
KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
- new int[] {ENCRYPTION_ALGORITHM_AES_CBC});
+ new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
defaults.putIntArray(
KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
- new int[] {ENCRYPTION_ALGORITHM_AES_CBC});
+ new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
defaults.putIntArray(
KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
new int[] {
- INTEGRITY_ALGORITHM_AES_XCBC_96,
- INTEGRITY_ALGORITHM_HMAC_SHA1_96,
- INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
- INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
- INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
+ SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
});
defaults.putIntArray(
KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY,
new int[] {
- PSEUDORANDOM_FUNCTION_HMAC_SHA1,
- PSEUDORANDOM_FUNCTION_AES128_XCBC,
- PSEUDORANDOM_FUNCTION_SHA2_256,
- PSEUDORANDOM_FUNCTION_SHA2_384,
- PSEUDORANDOM_FUNCTION_SHA2_512
+ SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+ SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
+ SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+ SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+ SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512
});
defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_EAP_ONLY);
@@ -4587,16 +4557,28 @@ public class CarrierConfigManager {
defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20);
defaults.putIntArray(
KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
- new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+ new int[] {
+ SaProposal.KEY_LEN_AES_128,
+ SaProposal.KEY_LEN_AES_192,
+ SaProposal.KEY_LEN_AES_256});
defaults.putIntArray(
KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
- new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+ new int[] {
+ SaProposal.KEY_LEN_AES_128,
+ SaProposal.KEY_LEN_AES_192,
+ SaProposal.KEY_LEN_AES_256});
defaults.putIntArray(
KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
- new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+ new int[] {
+ SaProposal.KEY_LEN_AES_128,
+ SaProposal.KEY_LEN_AES_192,
+ SaProposal.KEY_LEN_AES_256});
defaults.putIntArray(
KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
- new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+ new int[] {
+ SaProposal.KEY_LEN_AES_128,
+ SaProposal.KEY_LEN_AES_192,
+ SaProposal.KEY_LEN_AES_256});
defaults.putIntArray(
KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
@@ -4825,6 +4807,16 @@ public class CarrierConfigManager {
public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY =
"allowed_initial_attach_apn_types_string_array";
+ /**
+ * Indicates whether or not the carrier will provision merged carrier Wi-Fi offload networks.
+ * Such networks are considered part of the core carrier network.
+ *
+ * This configuration will be use to gate whether such configurations are allowed to the carrier
+ * and correspondingly enable UI elements which are required for such configurations.
+ */
+ public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL =
+ "carrier_provisions_wifi_merged_networks_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5011,6 +5003,9 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
@@ -5189,6 +5184,8 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
+ sDefaults.putBoolean(KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL, true);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
@@ -5364,6 +5361,13 @@ public class CarrierConfigManager {
// Default wifi configurations.
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
+ sDefaults.putInt(KEY_GBA_MODE_INT, GBA_ME);
+ sDefaults.putInt(KEY_GBA_UA_SECURITY_ORGANIZATION_INT,
+ UaSecurityProtocolIdentifier.ORG_3GPP);
+ sDefaults.putInt(KEY_GBA_UA_SECURITY_PROTOCOL_INT,
+ UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT);
+ sDefaults.putInt(KEY_GBA_UA_TLS_CIPHER_SUITE_INT, TlsParams.TLS_NULL_WITH_NULL_NULL);
+
sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1));
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
@@ -5385,6 +5389,8 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false);
sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY,
new String[]{"ia", "default", "ims", "mms", "dun", "emergency"});
+ sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false);
+ sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, "");
}
/**
diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.aidl b/telephony/java/android/telephony/LinkCapacityEstimate.aidl
new file mode 100644
index 000000000000..286f33fc9810
--- /dev/null
+++ b/telephony/java/android/telephony/LinkCapacityEstimate.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable LinkCapacityEstimate; \ No newline at end of file
diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.java b/telephony/java/android/telephony/LinkCapacityEstimate.java
new file mode 100644
index 000000000000..deeb80961c3c
--- /dev/null
+++ b/telephony/java/android/telephony/LinkCapacityEstimate.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Link Capacity Estimate from the modem
+ * @hide
+ */
+@SystemApi
+public final class LinkCapacityEstimate implements Parcelable {
+ /** A value indicates that the capacity estimate is not available */
+ public static final int INVALID = -1;
+
+ /**
+ * LCE for the primary network
+ */
+ public static final int LCE_TYPE_PRIMARY = 0;
+
+ /**
+ * LCE for the secondary network
+ */
+ public static final int LCE_TYPE_SECONDARY = 1;
+
+ /**
+ * Combined LCE for primary network and secondary network reported by the legacy modem
+ */
+ public static final int LCE_TYPE_COMBINED = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "LCE_TYPE_" }, value = {
+ LCE_TYPE_PRIMARY,
+ LCE_TYPE_SECONDARY,
+ LCE_TYPE_COMBINED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LceType {}
+
+ private final @LceType int mType;
+
+ /** Downlink capacity estimate in kbps */
+ private final int mDownlinkCapacityKbps;
+
+ /** Uplink capacity estimate in kbps */
+ private final int mUplinkCapacityKbps;
+
+ /**
+ * Constructor for link capacity estimate
+ */
+ public LinkCapacityEstimate(@LceType int type,
+ int downlinkCapacityKbps, int uplinkCapacityKbps) {
+ mDownlinkCapacityKbps = downlinkCapacityKbps;
+ mUplinkCapacityKbps = uplinkCapacityKbps;
+ mType = type;
+ }
+
+ /**
+ * @hide
+ */
+ public LinkCapacityEstimate(Parcel in) {
+ mDownlinkCapacityKbps = in.readInt();
+ mUplinkCapacityKbps = in.readInt();
+ mType = in.readInt();
+ }
+
+ /**
+ * Retrieves the type of LCE
+ * @return The type of link capacity estimate
+ */
+ public @LceType int getType() {
+ return mType;
+ }
+
+ /**
+ * Retrieves the downlink bandwidth in Kbps.
+ * This will be {@link #INVALID} if the network is not connected
+ * @return The estimated first hop downstream (network to device) bandwidth.
+ */
+ public int getDownlinkCapacityKbps() {
+ return mDownlinkCapacityKbps;
+ }
+
+ /**
+ * Retrieves the uplink bandwidth in Kbps.
+ * This will be {@link #INVALID} if the network is not connected
+ *
+ * @return The estimated first hop upstream (device to network) bandwidth.
+ */
+ public int getUplinkCapacityKbps() {
+ return mUplinkCapacityKbps;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{mType=")
+ .append(mType)
+ .append(", mDownlinkCapacityKbps=")
+ .append(mDownlinkCapacityKbps)
+ .append(", mUplinkCapacityKbps=")
+ .append(mUplinkCapacityKbps)
+ .append("}")
+ .toString();
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ * @hide
+ */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mDownlinkCapacityKbps);
+ dest.writeInt(mUplinkCapacityKbps);
+ dest.writeInt(mType);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == null || !(o instanceof LinkCapacityEstimate) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ LinkCapacityEstimate that = (LinkCapacityEstimate) o;
+ return mDownlinkCapacityKbps == that.mDownlinkCapacityKbps
+ && mUplinkCapacityKbps == that.mUplinkCapacityKbps
+ && mType == that.mType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDownlinkCapacityKbps, mUplinkCapacityKbps, mType);
+ }
+
+ public static final
+ @android.annotation.NonNull Parcelable.Creator<LinkCapacityEstimate> CREATOR =
+ new Parcelable.Creator() {
+ public LinkCapacityEstimate createFromParcel(Parcel in) {
+ return new LinkCapacityEstimate(in);
+ }
+
+ public LinkCapacityEstimate[] newArray(int size) {
+ return new LinkCapacityEstimate[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index cf4e6779b363..f7580d77186d 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -416,7 +416,7 @@ public class SubscriptionManager {
* <p>
* Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
* subscription cross sim calling enabled
- * {@link ImsMmTelManager#isCrossSimCallingEnabledByUser()}
+ * {@link ImsMmTelManager#isCrossSimCallingEnabled()}
* while your app is running. You can also use a {@link android.app.job.JobService}
* to ensure your app
* is notified of changes to the {@link Uri} even when it is not running.
@@ -602,6 +602,43 @@ public class SubscriptionManager {
public @interface SimDisplayNameSource {}
/**
+ * Device status is not shared to a remote party.
+ */
+ public static final int D2D_SHARING_DISABLED = 0;
+
+ /**
+ * Device status is shared with all numbers in the user's contacts.
+ */
+ public static final int D2D_SHARING_ALL_CONTACTS = 1;
+
+ /**
+ * Device status is shared with all starred contacts.
+ */
+ public static final int D2D_SHARING_STARRED_CONTACTS = 2;
+
+ /**
+ * Device status is shared whenever possible.
+ */
+ public static final int D2D_SHARING_ALL = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"D2D_SHARING_"},
+ value = {
+ D2D_SHARING_DISABLED,
+ D2D_SHARING_ALL_CONTACTS,
+ D2D_SHARING_STARRED_CONTACTS,
+ D2D_SHARING_ALL
+ })
+ public @interface DeviceToDeviceStatusSharing {}
+
+ /**
+ * TelephonyProvider column name for device to device sharing status.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING;
+
+ /**
* TelephonyProvider column name for the color of a SIM.
* <P>Type: INTEGER (int)</P>
*/
@@ -3374,6 +3411,36 @@ public class SubscriptionManager {
}
/**
+ * Set the device to device status sharing user preference for a subscription ID. The setting
+ * app uses this method to indicate with whom they wish to share device to device status
+ * information.
+ * @param sharing the status sharing preference
+ * @param subId the unique Subscription ID in database
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharing int sharing,
+ int subId) {
+ if (VDBG) {
+ logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + subId);
+ }
+ setSubscriptionPropertyHelper(subId, "setDeviceToDeviceSharingStatus",
+ (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subId));
+ }
+
+ /**
+ * Returns the user-chosen device to device status sharing preference
+ * @param subId Subscription id of subscription
+ * @return The device to device status sharing preference
+ */
+ public @DeviceToDeviceStatusSharing int getDeviceToDeviceStatusSharing(int subId) {
+ if (VDBG) {
+ logd("[getDeviceToDeviceStatusSharing] + subId: " + subId);
+ }
+ return getIntegerSubscriptionProperty(subId, D2D_STATUS_SHARING, D2D_SHARING_DISABLED,
+ mContext);
+ }
+
+ /**
* DO NOT USE.
* This API is designed for features that are not finished at this point. Do not call this API.
* @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c48bd211fac2..962200b82a81 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -139,6 +139,8 @@ import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Provides access to information about the telephony services on
@@ -3147,6 +3149,10 @@ public class TelephonyManager {
return NETWORK_TYPE_BITMASK_LTE_CA;
case NETWORK_TYPE_NR:
return NETWORK_TYPE_BITMASK_NR;
+ case NETWORK_TYPE_IWLAN:
+ return NETWORK_TYPE_BITMASK_IWLAN;
+ case NETWORK_TYPE_IDEN:
+ return (1 << (NETWORK_TYPE_IDEN - 1));
default:
return NETWORK_TYPE_BITMASK_UNKNOWN;
}
@@ -8642,8 +8648,8 @@ public class TelephonyManager {
public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
/**
- * Set the allowed network types of the device and
- * provide the reason triggering the allowed network change.
+ * Set the allowed network types of the device and provide the reason triggering the allowed
+ * network change.
* This can be called for following reasons
* <ol>
* <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
@@ -8655,10 +8661,15 @@ public class TelephonyManager {
* </ol>
* This API will result in allowing an intersection of allowed network types for all reasons,
* including the configuration done through other reasons.
+ *
+ * The functionality of this API with the parameter
+ * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API
+ * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of
+ * {@link TelephonyManager#setAllowedNetworkTypes}.
* <p>
* If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
* ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
- * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
+ * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
* setPreferredNetworkTypesBitmap is used instead.
*
* @param reason the reason the allowed network type change is taking place
@@ -8698,21 +8709,17 @@ public class TelephonyManager {
* {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
* specific reason.
*
- * <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
- * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
- *
* @param reason the reason the allowed network type change is taking place
* @return the allowed network type bitmask
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(
enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
+ @SystemApi
public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
@AllowedNetworkTypesReason int reason) {
if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -8757,6 +8764,25 @@ public class TelephonyManager {
}
/**
+ * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}.
+ *
+ * @param networkTypeBitmask The bitmask of allowed network types.
+ * @return the name of the allowed network types
+ * @hide
+ */
+ public static String convertNetworkTypeBitmaskToString(
+ @NetworkTypeBitMask long networkTypeBitmask) {
+ String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+ .filter(x -> {
+ return (networkTypeBitmask & getBitMaskForNetworkType(x))
+ == getBitMaskForNetworkType(x);
+ })
+ .mapToObj(x -> getNetworkTypeName(x))
+ .collect(Collectors.joining("|"));
+ return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName;
+ }
+
+ /**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -9135,9 +9161,7 @@ public class TelephonyManager {
/**
* Set the user-set status for enriched calling with call composer.
*
- * @param status user-set status for enriched calling with call composer;
- * it must be either {@link #CALL_COMPOSER_STATUS_ON} or
- * {@link #CALL_COMPOSER_STATUS_OFF}.
+ * @param status user-set status for enriched calling with call composer.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -9956,7 +9980,8 @@ public class TelephonyManager {
}
/**
- * Sets the roaming mode for CDMA phone to the given mode {@code mode}.
+ * Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not
+ * CDMA capable, this method does nothing.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -9979,6 +10004,7 @@ public class TelephonyManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCdmaRoamingMode(@CdmaRoamingMode int mode) {
+ if (getPhoneType() != PHONE_TYPE_CDMA) return;
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -10059,7 +10085,8 @@ public class TelephonyManager {
}
/**
- * Sets the subscription mode for CDMA phone to the given mode {@code mode}.
+ * Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not
+ * CDMA capable, this method does nothing.
*
* @param mode CDMA subscription mode.
* @throws SecurityException if the caller does not have the permission.
@@ -10078,6 +10105,7 @@ public class TelephonyManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCdmaSubscriptionMode(@CdmaSubscription int mode) {
+ if (getPhoneType() != PHONE_TYPE_CDMA) return;
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -12579,6 +12607,7 @@ public class TelephonyManager {
NETWORK_TYPE_BITMASK_LTE,
NETWORK_TYPE_BITMASK_LTE_CA,
NETWORK_TYPE_BITMASK_NR,
+ NETWORK_TYPE_BITMASK_IWLAN
})
public @interface NetworkTypeBitMask {}
@@ -14478,34 +14507,6 @@ public class TelephonyManager {
}
/**
- * Get carrier bandwidth. In case of Dual connected network this will report
- * bandwidth per primary and secondary network. It is possible that
- * some modems may not fill secondary carrier bandwidth.
- * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
- * @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @NonNull
- public CarrierBandwidth getCarrierBandwidth() {
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getCarrierBandwidth(getSubId());
- } else {
- throw new IllegalStateException("telephony service is null.");
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "getCarrierBandwidth RemoteException", ex);
- ex.rethrowFromSystemServer();
- }
-
- //Should not reach. Adding return statement to make compiler happy
- return null;
- }
-
- /**
* Called when userActivity is signalled in the power manager.
* This should only be called from system Uid.
* @hide
@@ -14609,6 +14610,10 @@ public class TelephonyManager {
/**
* Enable/Disable E-UTRA-NR Dual Connectivity.
*
+ * This api is supported only if
+ * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+ * returns true.
* @param nrDualConnectivityState expected NR dual connectivity state
* This can be passed following states
* <ol>
@@ -14618,12 +14623,14 @@ public class TelephonyManager {
* {@link #NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
* </ol>
* @return operation result.
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
public @EnableNrDualConnectivityResult int setNrDualConnectivityState(
@NrDualConnectivityState int nrDualConnectivityState) {
try {
@@ -14643,15 +14650,21 @@ public class TelephonyManager {
/**
* Is E-UTRA-NR Dual Connectivity enabled.
+ * This api is supported only if
+ * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+ * returns true.
* @return true if dual connectivity is enabled else false. Enabled state does not mean dual
* connectivity is active. It means the device is allowed to connect to both primary and
* secondary cell.
- * <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
public boolean isNrDualConnectivityEnabled() {
try {
ITelephony telephony = getITelephony();
@@ -14903,11 +14916,23 @@ public class TelephonyManager {
public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
"CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+ /**
+ * Indicates whether {@link #setNrDualConnectivityState()} and
+ * {@link #isNrDualConnectivityEnabled()} ()} are available. See comments
+ * on respective methods for more information.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE =
+ "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
+ CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE
})
public @interface RadioInterfaceCapability {}
@@ -15219,13 +15244,17 @@ public class TelephonyManager {
* </ul>
* @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
* #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
- * @param nafId Network Application Function(NAF) fully qualified domain name and
- * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
- * part is the constant string "3GPP-bootstrapping" (GBA_ME),
- * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
- * and the latter part shall be the FQDN of the NAF (e.g.
- * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com",
- * or "3GPP-bootstrapping-digest@naf1.operator.com").
+ * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain
+ * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts
+ * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME),
+ * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest).
+ * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used
+ * for the authentication, which may be set the same as the resource that the application is
+ * going to access. For example, the nafId can be
+ * "https://3GPP-bootstrapping@naf1.operator.com",
+ * "https://3GPP-bootstrapping-uicc@naf1.operator.com",
+ * "https://3GPP-bootstrapping-digest@naf1.operator.com",
+ * "ftps://3GPP-bootstrapping-digest@naf1.operator.com".
* @param securityProtocol Security protocol identifier between UE and NAF.  See
* 3GPP TS 33.220 Annex H. Application can use
   * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
@@ -15425,7 +15454,9 @@ public class TelephonyManager {
public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
/**
- * The unattended reboot was not prepared due to generic error.
+ * The unattended reboot was not prepared due to a non-recoverable error. After this error,
+ * the client that manages the unattended reboot should not try to invoke the API again
+ * until the next power cycle.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a76422977cb6..ffe5399e406b 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -18,6 +18,7 @@
package android.telephony.data;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -29,6 +30,7 @@ import android.telephony.DataFailCause;
import android.telephony.data.ApnSetting.ProtocolType;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -812,11 +814,19 @@ public final class DataCallResponse implements Parcelable {
/**
* Set pdu session id.
+ * <p/>
+ * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
+ * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}.
*
* @param pduSessionId Pdu Session Id of the data call.
* @return The same instance of the builder.
*/
- public @NonNull Builder setPduSessionId(int pduSessionId) {
+ public @NonNull Builder setPduSessionId(
+ @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) {
+ Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET,
+ "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET);
+ Preconditions.checkArgument(pduSessionId <= 15,
+ "pduSessionId must be less than or equal to 15.");
mPduSessionId = pduSessionId;
return this;
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f5f29c65b7cd..048b3297a1b4 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -41,6 +41,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Base class of data service. Services that extend DataService must register the service in
@@ -284,11 +285,11 @@ public abstract class DataService extends Service {
*
* Any resources being transferred cannot be released while a
* handover is underway.
- *
+ * <p/>
* If a handover was unsuccessful, then the framework calls
* {@link DataService#cancelHandover}. The target transport retains ownership over any of
* the resources being transferred.
- *
+ * <p/>
* If a handover was successful, the framework calls {@link DataService#deactivateDataCall}
* with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns
* the transferred resources and is responsible for releasing them.
@@ -299,21 +300,27 @@ public abstract class DataService extends Service {
* @hide
*/
public void startHandover(int cid, @NonNull DataServiceCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
// The default implementation is to return unsupported.
- if (callback != null) {
- Log.d(TAG, "startHandover: " + cid);
- callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
- } else {
- Log.e(TAG, "startHandover: " + cid + ", callback is null");
- }
+ Log.d(TAG, "startHandover: " + cid);
+ callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
/**
* Indicates that a handover was cancelled after a call to
* {@link DataService#startHandover}. This is called on the source transport.
- *
+ * <p/>
* Since the handover was unsuccessful, the source transport retains ownership over any of
* the resources being transferred and is still responsible for releasing them.
+ * <p/>
+ * The handover can be cancelled up until either:
+ * <ul><li>
+ * The handover was successful after receiving a successful response from
+ * {@link DataService#setupDataCall} on the target transport.
+ * </li><li>
+ * The data call on the source transport was lost.
+ * </li>
+ * </ul>
*
* @param cid The identifier of the data call which is provided in {@link DataCallResponse}
* @param callback The result callback for this request.
@@ -321,13 +328,10 @@ public abstract class DataService extends Service {
* @hide
*/
public void cancelHandover(int cid, @NonNull DataServiceCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
// The default implementation is to return unsupported.
- if (callback != null) {
- Log.d(TAG, "cancelHandover: " + cid);
- callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
- } else {
- Log.e(TAG, "cancelHandover: " + cid + ", callback is null");
- }
+ Log.d(TAG, "cancelHandover: " + cid);
+ callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
/**
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index ca1f861f9808..363e47a6d242 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -254,15 +254,15 @@ public class DataServiceCallback {
}
/**
- * The APN is throttled for the duration specified in
- * {@link DataCallResponse#getRetryDurationMillis}. Calling this method unthrottles that
- * APN.
+ * Unthrottles the APN on the current transport. There is no matching "APN throttle" method.
+ * Instead, the APN is throttled for the time specified in
+ * {@link DataCallResponse#getRetryDurationMillis}.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
* @param apn Access Point Name defined by the carrier.
*/
- public void onApnUnthrottled(@NonNull String apn) {
+ public void onApnUnthrottled(final @NonNull String apn) {
if (mCallback != null) {
try {
if (DBG) Rlog.d(TAG, "onApnUnthrottled");
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
index 041edc00c4d2..406c38bf60ef 100644
--- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -184,16 +184,6 @@ public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessi
mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
}
- /**
- * Creates attributes based off of a parcel
- * @param in the parcel
- * @return the attributes
- */
- @NonNull
- public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) {
- return new EpsBearerQosSessionAttributes(in);
- }
-
@Override
public int describeContents() {
return 0;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9bb4db8edf79..486f74632ca2 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -277,6 +277,10 @@ public final class ImsCallProfile implements Parcelable {
* server infrastructure to get the picture. It can be set via
* {@link #setCallExtra(String, String)}.
*
+ * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent
+ * directly to the network for consumption by the called party or forwarded directly from the
+ * network to the platform for caching and download.
+ *
* Reference: RCC.20 Section 2.4.3.2
*/
public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
@@ -729,6 +733,10 @@ public final class ImsCallProfile implements Parcelable {
/**
* Set the call extra value (Parcelable), given the call extra name.
+ *
+ * Note that the {@link Parcelable} provided must be a class defined in the Android API surface,
+ * as opposed to a class defined by your app.
+ *
* @param name call extra name
* @param parcelable call extra value
*/
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index fcb4782c7f62..4b2829685d66 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -1018,7 +1018,7 @@ public class ImsMmTelManager implements RegistrationManager {
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
- public boolean isCrossSimCallingEnabledByUser() throws ImsException {
+ public boolean isCrossSimCallingEnabled() throws ImsException {
ITelephony iTelephony = getITelephony();
if (iTelephony == null) {
throw new ImsException("Could not find Telephony Service.",
@@ -1058,7 +1058,7 @@ public class ImsMmTelManager implements RegistrationManager {
* the IMS service is not available.
* @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
* false otherwise
- * @see #isCrossSimCallingEnabledByUser()
+ * @see #isCrossSimCallingEnabled()
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 85cd81bb4eb5..abc5606e6743 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -1010,6 +1010,16 @@ public class ProvisioningManager {
}
}
+ @Override
+ public void onPreProvisioningReceived(byte[] configXml) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private void setExecutor(Executor executor) {
mExecutor = executor;
}
@@ -1022,7 +1032,7 @@ public class ProvisioningManager {
* due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
* server) or other operator defined triggers. If RCS provisioning is already
* completed at the time of callback registration, then this method shall be
- * invoked with the current configuration
+ * invoked with the current configuration.
* @param configXml The RCS configuration XML received by OTA. It is defined
* by GSMA RCC.07.
*/
@@ -1055,6 +1065,20 @@ public class ProvisioningManager {
*/
public void onRemoved() {}
+ /**
+ * Some carriers using ACS (auto configuration server) may send a carrier-specific
+ * pre-provisioning configuration XML if the user has not been provisioned for RCS
+ * services yet. When this provisioning XML is received, the framework will move
+ * into a "not provisioned" state for RCS. In order for provisioning to proceed,
+ * the application must parse this configuration XML and perform the carrier specific
+ * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration}
+ * must be called in order for the device to move out of this state and try to fetch
+ * the RCS provisioning information.
+ *
+ * @param configXml the pre-provisioning config in carrier specified format.
+ */
+ public void onPreProvisioningReceived(@NonNull byte[] configXml) {}
+
/**@hide*/
public final IRcsConfigCallback getBinder() {
return mBinder;
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index cedf48b0b8e1..9c28c36521f5 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -25,7 +25,6 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -389,22 +388,6 @@ public final class RcsContactPresenceTuple implements Parcelable {
* The optional timestamp indicating the data and time of the status change of this tuple.
* Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
* string per RFC3339.
- * @hide
- */
- public @NonNull Builder setTimestamp(@NonNull String timestamp) {
- try {
- mPresenceTuple.mTimestamp =
- DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
- } catch (DateTimeParseException e) {
- Log.d(LOG_TAG, "Parse timestamp failed " + e);
- }
- return this;
- }
-
- /**
- * The optional timestamp indicating the data and time of the status change of this tuple.
- * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
- * string per RFC3339.
*/
public @NonNull Builder setTime(@NonNull Instant timestamp) {
mPresenceTuple.mTimestamp = timestamp;
@@ -534,14 +517,6 @@ public final class RcsContactPresenceTuple implements Parcelable {
return mContactUri;
}
- /**
- * @return the timestamp element contained in the tuple if it exists
- * @hide
- */
- public @Nullable String getTimestamp() {
- return (mTimestamp == null) ? null : mTimestamp.toString();
- }
-
/** @return the timestamp element contained in the tuple if it exists */
public @Nullable Instant getTime() {
return mTimestamp;
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 815c08d120c2..dd9102699529 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -439,8 +439,7 @@ public class RcsUceAdapter {
/**
* The pending request has resulted in an error and may need to be retried, depending on the
- * error code. The callback {@link #onCapabilitiesReceived(List)}
- * for each contacts is required to be called before {@link #onError} is called.
+ * error code.
* @param errorCode The reason for the framework being unable to process the request.
* @param retryIntervalMillis The time in milliseconds the requesting application should
* wait before retrying, if non-zero.
@@ -487,93 +486,6 @@ public class RcsUceAdapter {
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
- @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
- Manifest.permission.READ_CONTACTS})
- public void requestCapabilities(@NonNull List<Uri> contactNumbers,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull CapabilitiesCallback c) throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Must include a non-null Executor.");
- }
- if (contactNumbers == null) {
- throw new IllegalArgumentException("Must include non-null contact number list.");
- }
-
- IImsRcsController imsRcsController = getIImsRcsController();
- if (imsRcsController == null) {
- Log.e(TAG, "requestCapabilities: IImsRcsController is null");
- throw new ImsException("Can not find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
- }
-
- IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
- @Override
- public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
- @Override
- public void onComplete() {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> c.onComplete());
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
- @Override
- public void onError(int errorCode, long retryAfterMilliseconds) {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds));
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
- };
-
- try {
- imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
- mContext.getAttributionTag(), contactNumbers, internalCallback);
- } catch (ServiceSpecificException e) {
- throw new ImsException(e.toString(), e.errorCode);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
- throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
- }
- }
-
- /**
- * Request the User Capability Exchange capabilities for one or more contacts.
- * <p>
- * This will return the cached capabilities of the contact and will not perform a capability
- * poll on the network unless there are contacts being queried with stale information.
- * <p>
- * Be sure to check the availability of this feature using
- * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
- * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
- *
- * @param contactNumbers A list of numbers that the capabilities are being requested for.
- * @param executor The executor that will be used when the request is completed and the
- * {@link CapabilitiesCallback} is called.
- * @param c A one-time callback for when the request for capabilities completes or there is an
- * error processing the request.
- * @throws ImsException if the subscription associated with this instance of
- * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
- * available. This can happen if the ImsService has crashed, for example, or if the subscription
- * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
- * @hide
- */
@SystemApi
@RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
Manifest.permission.READ_CONTACTS})
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
index 5a8973e37bce..d0853d1846ac 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
@@ -25,5 +25,6 @@ oneway interface IRcsConfigCallback {
void onAutoConfigurationErrorReceived(int errorCode, String errorString);
void onConfigurationReset();
void onRemoved();
+ void onPreProvisioningReceived(in byte[] config);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 21aeb64bb417..d75da9035124 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -552,11 +552,34 @@ public class ImsConfigImplBase {
}
mRcsCallbacks.broadcastAction(c -> {
try {
- //TODO compressed by default?
c.onAutoConfigurationErrorReceived(errorCode, errorString);
} catch (RemoteException e) {
Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
}
});
}
+
+ /**
+ * Notifies application that pre-provisioning config is received.
+ *
+ * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific
+ * pre-provisioning configuration XML if the user has not been provisioned for RCS
+ * services yet. When such provisioning XML is received, ACS client must call this
+ * method to notify the application with the XML.
+ *
+ * @param configXml the pre-provisioning config in carrier specified format.
+ */
+ public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) {
+ // can be null in testing
+ if (mRcsCallbacks == null) {
+ return;
+ }
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onPreProvisioningReceived(configXml);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+ }
+ });
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 00c91681d9ea..03e17fbc2c0d 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -386,41 +386,6 @@ public class RcsCapabilityExchangeImplBase {
* {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
* framework to finish listening for NOTIFY responses.
*
- * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
- * capabilities for.
- * @param cb The callback of the subscribe request.
- * @hide
- */
- // executor used is defined in the constructor.
- @SuppressLint("ExecutorRegistration")
- public void subscribeForCapabilities(@NonNull List<Uri> uris,
- @NonNull SubscribeResponseCallback cb) {
- // Stub - to be implemented by service
- Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
- try {
- cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
- } catch (ImsException e) {
- // Do not do anything, this is a stub implementation.
- }
- }
-
- /**
- * The user capabilities of one or multiple contacts have been requested by the framework.
- * <p>
- * The implementer must follow up this call with an
- * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
- * The response from the network to the SUBSCRIBE request must be sent back to the framework
- * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
- * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
- * sent back to the framework using
- * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
- * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
- * should be called with the presence information for the contacts specified.
- * <p>
- * Once the subscription is terminated,
- * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
- * framework to finish listening for NOTIFY responses.
- *
* @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
* UCE capabilities for.
* @param cb The callback of the subscribe request.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 571efcee0e15..9493c76d9a57 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -300,4 +300,6 @@ interface ISub {
boolean canDisablePhysicalSubscription();
int setUiccApplicationsEnabled(boolean enabled, int subscriptionId);
+
+ int setDeviceToDeviceStatusSharing(int sharing, int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 40b86966d0e8..96af172e489e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -31,7 +31,6 @@ import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telephony.CallForwardingInfo;
-import android.telephony.CarrierBandwidth;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
@@ -55,6 +54,7 @@ import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -2238,12 +2238,6 @@ interface ITelephony {
boolean isNrDualConnectivityEnabled(int subId);
/**
- * Get carrier bandwidth per primary and secondary carrier
- * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
- */
- CarrierBandwidth getCarrierBandwidth(int subId);
-
- /**
* Checks whether the device supports the given capability on the radio interface.
*
* @param capability the name of the capability
@@ -2381,6 +2375,41 @@ interface ITelephony {
void setDeviceUceEnabled(boolean isEnabled);
/**
+ * Add feature tags to the IMS registration being tracked by UCE and potentially
+ * generate a new PUBLISH to the network.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability addUceRegistrationOverrideShell(int subId, in List<String> featureTags);
+
+ /**
+ * Remove feature tags from the IMS registration being tracked by UCE and potentially
+ * generate a new PUBLISH to the network.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+ in List<String> featureTags);
+
+ /**
+ * Clear overridden feature tags in the IMS registration being tracked by UCE and potentially
+ * generate a new PUBLISH to the network.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability clearUceRegistrationOverrideShell(int subId);
+
+ /**
+ * Get the latest RcsContactUceCapability structure that is used in SIP PUBLISH procedures.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId);
+
+ /**
+ * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+ * device does not have an active PUBLISH.
+ * Note: This is designed for a SHELL command only.
+ */
+ String getLastUcePidfXmlShell(int subId);
+
+ /**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
*/
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index b2719fbcac82..896ec9ae922c 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -11,6 +11,8 @@
<option name="force-skip-system-props" value="true" />
<!-- set WM tracing verbose level to all -->
<option name="run-command" value="cmd window tracing level all" />
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame" />
<!-- restart launcher to activate TAPL -->
<option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
</target_preparer>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index a8b7b057fe24..e6a4501fc529 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -35,15 +35,6 @@ fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
}
}
-@JvmOverloads
-fun FlickerTestParameter.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows: List<String> = emptyList()
-) {
- assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
- }
-}
-
fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
assertWm {
this.showsAppWindowOnTop(testApp.getPackage())
@@ -184,15 +175,6 @@ fun FlickerTestParameter.statusBarLayerRotatesScales(
}
}
-@JvmOverloads
-fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers: List<String> = emptyList()
-) {
- assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
- }
-}
-
fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) {
assertLayers {
this.isVisible(WALLPAPER_TITLE)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index fbf18d45afd8..c92d40cdd555 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,37 +16,12 @@
package com.android.server.wm.flicker.close
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- this.setRotation(testSpec.config.startRotation)
- testApp.launchViaIntent(wmHelper)
- }
- }
+class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
transitions {
device.pressBack()
wmHelper.waitForHomeActivityVisible()
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun launcherReplacesAppWindowAsTopWindow() =
- testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest(bugId = 173684672)
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 173684672)
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 08d2b7c206bf..1f880f61d65e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,37 +16,12 @@
package com.android.server.wm.flicker.close
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.config.startRotation)
- }
- }
+class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun launcherReplacesAppWindowAsTopWindow() =
- testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(
- testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest(bugId = 173689015)
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 173689015)
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
new file mode 100644
index 000000000000..e118363de58d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.close
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
+import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
+import org.junit.Test
+
+abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+ protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ }
+ }
+ }
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ transition(testSpec.config)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerRotatesAndScales() {
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun noUncoveredRegions() {
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ open fun launcherReplacesAppWindowAsTopWindow() {
+ testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+ }
+
+ @Presubmit
+ @Test
+ open fun wallpaperWindowBecomesVisible() {
+ testSpec.wallpaperWindowBecomesVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun wallpaperLayerReplacesAppLayer() {
+ testSpec.wallpaperLayerReplacesAppLayer(testApp)
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index f7e749311442..fad25b4fa0b9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -16,31 +16,13 @@
package com.android.server.wm.flicker.helpers
-import android.os.RemoteException
-import android.view.Surface
import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
/**
* Changes the device [rotation] and wait for the rotation animation to complete
*
* @param rotation New device rotation
*/
-fun Flicker.setRotation(rotation: Int) {
- try {
- when (rotation) {
- Surface.ROTATION_270 -> device.setOrientationRight()
- Surface.ROTATION_90 -> device.setOrientationLeft()
- Surface.ROTATION_0 -> device.setOrientationNatural()
- else -> device.setOrientationNatural()
- }
-
- wmHelper.waitForRotation(rotation)
- wmHelper.waitForNavBarStatusBarVisible()
- wmHelper.waitForAppTransitionIdle()
-
- // Ensure WindowManagerService wait until all animations have completed
- instrumentation.uiAutomation.syncInputTransactions()
- } catch (e: RemoteException) {
- throw RuntimeException(e)
- }
-} \ No newline at end of file
+fun Flicker.setRotation(rotation: Int) =
+ ChangeDisplayOrientationRule.setRotation(rotation, instrumentation, wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 47eaddfa1f3a..b8b0e1e53bbd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -17,10 +17,7 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -29,20 +26,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import org.junit.Assume
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,23 +56,15 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
testApp.launchViaIntent(wmHelper)
testApp.openIME(device, wmHelper)
- this.setRotation(testSpec.config.startRotation)
}
}
teardown {
test {
- testApp.exit()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(Surface.ROTATION_0)
+ testApp.exit(wmHelper)
}
}
transitions {
@@ -89,76 +73,68 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
}
}
- @Postsubmit
+ @Presubmit
@Test
fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
- @Postsubmit
+ @Presubmit
@Test
fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
- @Postsubmit
+ @Presubmit
@Test
fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
- @Postsubmit
+ @Presubmit
@Test
fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
- @Postsubmit
+ @Presubmit
@Test
fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
- @Postsubmit
+ @Presubmit
@Test
fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
}
@Presubmit
@Test
fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
}
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 26afb794bb06..cb896bf93df6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -19,7 +19,6 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -28,20 +27,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import org.junit.Assume
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -63,22 +57,15 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
testApp.launchViaIntent(wmHelper)
testApp.openIME(device, wmHelper)
- this.setRotation(testSpec.config.startRotation)
}
}
teardown {
test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
+ testApp.exit(wmHelper)
}
}
transitions {
@@ -99,8 +86,13 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Presubmit
@Test
@@ -126,28 +118,12 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
@Presubmit
@Test
fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
@@ -162,15 +138,10 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
- }
-
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ }
}
companion object {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 2c4c627a444d..6b8bf63aa926 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -18,7 +18,7 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -27,18 +27,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.Assume
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,13 +57,9 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent()
- this.setRotation(testSpec.config.startRotation)
}
eachRun {
testApp.openIME(device, wmHelper)
@@ -74,8 +67,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
}
teardown {
test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
+ testApp.exit(wmHelper)
}
}
transitions {
@@ -94,8 +86,13 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Presubmit
@Test
@@ -115,18 +112,39 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() =
+ fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ }
@Presubmit
@Test
- fun statusBarLayerRotatesScales() =
+ fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ }
@Presubmit
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 2bcdcd9c8219..9b37cafa74c3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -28,19 +28,14 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import org.junit.Assume
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,15 +56,9 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.config.startRotation)
testApp.openIME(device, wmHelper)
}
}
@@ -85,7 +74,6 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
}
test {
testApp.exit()
- this.setRotation(Surface.ROTATION_0)
}
}
}
@@ -101,8 +89,13 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Presubmit
@Test
@@ -141,29 +134,31 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ }
}
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5,
- supportedRotations = listOf(Surface.ROTATION_0))
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 6b2b930128fa..d39044ab285d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -27,23 +27,17 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.layerAlwaysVisible
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,13 +59,9 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.config.startRotation)
}
}
transitions {
@@ -83,7 +73,6 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
test {
testApp.exit()
- this.setRotation(Surface.ROTATION_0)
}
}
}
@@ -128,48 +117,44 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
}
@Presubmit
@Test
fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
}
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest
+ @Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5,
- supportedRotations = listOf(Surface.ROTATION_0))
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 0cd5d7999a58..cbf3069c3009 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
+import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -29,24 +29,19 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.testapp.ActivityOptions
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -69,11 +64,8 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent(wmHelper)
testApp.openIME(device, wmHelper)
}
@@ -90,7 +82,6 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
teardown {
test {
- this.setRotation(Surface.ROTATION_0)
testApp.exit()
}
}
@@ -107,8 +98,11 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
@Presubmit
@Test
@@ -149,42 +143,35 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
@Presubmit
@Test
fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 1)
+ .getConfigNonRotationTests(
+ repetitions = 1,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 74f002d67229..a9888b1876b7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,37 +16,14 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,120 +36,32 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
this.setRotation(testSpec.config.startRotation)
}
}
- transitions {
- testApp.launchViaIntent(wmHelper)
- // wmHelper.waitForFullScreenApp(testApp.component)
- }
teardown {
eachRun {
- testApp.exit()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(Surface.ROTATION_0)
+ testApp.exit(wmHelper)
}
}
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowReplacesLauncherAsTopWindow() =
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
- @Presubmit
- @Test
- // During testing the launcher is always in portrait mode
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun appLayerReplacesWallpaperLayer() =
- testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 18fac6a82de7..1d8fb028ed20 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,36 +16,18 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,18 +43,12 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent(wmHelper)
}
eachRun {
@@ -87,99 +63,33 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
device.reopenAppFromOverview(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
}
- teardown {
- test {
- testApp.exit()
- }
- }
}
- }
- @Presubmit
+ @Postsubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
- @Presubmit
+ @Postsubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Test
- fun appWindowReplacesLauncherAsTopWindow() =
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
- @Test
- fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
- @Presubmit
- @Test
- fun appLayerReplacesWallpaperLayer() =
- testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun wallpaperWindowBecomesInvisible() {
+ testSpec.wallpaperWindowBecomesInvisible()
}
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
+ override fun navBarLayerRotatesAndScales() {
Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerIsAlwaysVisible()
- }
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerIsAlwaysVisible()
- }
-
- @Presubmit
- @Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
-
- @FlakyTest
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.navBarLayerRotatesAndScales()
}
@FlakyTest
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
+ fun navBarLayerRotatesAndScales_flaky() {
Assume.assumeTrue(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.navBarLayerRotatesAndScales()
}
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
new file mode 100644
index 000000000000..cd5c61a5a927
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
+import org.junit.Test
+
+abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ }
+ }
+ }
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ transition(testSpec.config)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerRotatesAndScales() {
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
+
+ @Presubmit
+ @Test
+ // During testing the launcher is always in portrait mode
+ open fun noUncoveredRegions() {
+ testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun focusChanges() {
+ testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
+
+ @Presubmit
+ @Test
+ open fun appLayerReplacesWallpaperLayer() {
+ testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+ }
+
+ @Presubmit
+ @Test
+ open fun appWindowReplacesLauncherAsTopWindow() {
+ testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+ }
+
+ @Presubmit
+ @Test
+ open fun wallpaperWindowBecomesInvisible() {
+ testSpec.wallpaperWindowBecomesInvisible()
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index b61310aa4bd8..dcc64c94e0c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,36 +16,14 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -58,20 +36,13 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent(wmHelper)
- // wmHelper.waitForFullScreenApp(testApp.component)
}
eachRun {
device.pressHome()
@@ -79,99 +50,23 @@ class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
this.setRotation(testSpec.config.startRotation)
}
}
- transitions {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- }
teardown {
eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @FlakyTest
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowReplacesLauncherAsTopWindow() =
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
- @Presubmit
- @Test
- // During testing the launcher is always in portrait mode
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun appLayerReplacesWallpaperLayer() =
- testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests()
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 6985b360c9cc..f037f1d19583 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,12 +17,14 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,6 +53,26 @@ class ChangeAppRotationTest(
}
}
+ @FlakyTest(bugId = 151179149)
+ @Test
+ override fun focusDoesNotChange() {
+ super.focusDoesNotChange()
+ }
+
+ @Presubmit
+ @Test
+ override fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ super.navBarLayerRotatesAndScales()
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ fun navBarLayerRotatesAndScales_flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ super.navBarLayerRotatesAndScales()
+ }
+
@Presubmit
@Test
fun screenshotLayerBecomesInvisible() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 9d78eb86539a..6d2a9238bed5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -18,7 +18,6 @@ package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -28,18 +27,14 @@ import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import org.junit.Test
abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
@@ -50,12 +45,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
this.setRotation(testSpec.config.startRotation)
}
@@ -83,13 +73,13 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
testSpec.navBarWindowIsAlwaysVisible()
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
open fun navBarLayerIsAlwaysVisible() {
testSpec.navBarLayerIsAlwaysVisible()
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(
@@ -102,28 +92,33 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
testSpec.statusBarWindowIsAlwaysVisible()
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
open fun statusBarLayerIsAlwaysVisible() {
testSpec.statusBarLayerIsAlwaysVisible()
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(
testSpec.config.startRotation, testSpec.config.endRotation)
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
@Presubmit
@@ -133,13 +128,13 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
testSpec.config.endRotation, allStates = false)
}
- @FlakyTest(bugId = 151179149)
+ @Presubmit
@Test
open fun focusDoesNotChange() {
testSpec.focusDoesNotChange()
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
@@ -147,7 +142,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
}
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 45d3006b9481..fe444bdecba7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -22,7 +22,6 @@ import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.layerAlwaysVisible
@@ -61,26 +60,24 @@ class SeamlessAppRotationTest(
@FlakyTest(bugId = 140855415)
@Test
- override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+ override fun navBarLayerRotatesAndScales() {
+ super.navBarLayerRotatesAndScales()
+ }
@FlakyTest(bugId = 140855415)
@Test
- override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
-
- @FlakyTest(bugId = 147659548)
- @Test
- override fun noUncoveredRegions() = super.noUncoveredRegions()
+ override fun statusBarLayerRotatesScales() {
+ super.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
- fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+ fun appLayerAlwaysVisible() {
+ testSpec.layerAlwaysVisible(testApp.`package`)
+ }
@Presubmit
@Test
- fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
-
- @FlakyTest(bugId = 147659548)
- @Test
fun appLayerRotates() {
testSpec.assertLayers {
this.coversExactly(startingPos, testApp.`package`)
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 335c8d0127eb..eacf5b287a2e 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,14 +9,17 @@ package {
android_test {
name: "InputTests",
- srcs: ["src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
platform_apis: true,
certificate: "platform",
static_libs: [
- "androidx.test.ext.junit",
- "androidx.test.rules",
- "truth-prebuilt",
- "ub-uiautomator",
- ],
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "ub-uiautomator",
+ ],
test_suites: ["device-tests"],
}
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
new file mode 100644
index 000000000000..63500774816a
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputDeviceTest {
+ private static final float DELTA = 0.01f;
+ private static final int DEVICE_ID = 1000;
+
+ private void assertMotionRangeEquals(InputDevice.MotionRange range,
+ InputDevice.MotionRange outRange) {
+ assertEquals(range.getAxis(), outRange.getAxis());
+ assertEquals(range.getSource(), outRange.getSource());
+ assertEquals(range.getMin(), outRange.getMin(), DELTA);
+ assertEquals(range.getMax(), outRange.getMax(), DELTA);
+ assertEquals(range.getFlat(), outRange.getFlat(), DELTA);
+ assertEquals(range.getFuzz(), outRange.getFuzz(), DELTA);
+ assertEquals(range.getResolution(), outRange.getResolution(), DELTA);
+ }
+
+ private void assertDeviceEquals(InputDevice device, InputDevice outDevice) {
+ assertEquals(device.getId(), outDevice.getId());
+ assertEquals(device.getGeneration(), outDevice.getGeneration());
+ assertEquals(device.getControllerNumber(), outDevice.getControllerNumber());
+ assertEquals(device.getName(), outDevice.getName());
+ assertEquals(device.getVendorId(), outDevice.getVendorId());
+ assertEquals(device.getProductId(), outDevice.getProductId());
+ assertEquals(device.getDescriptor(), outDevice.getDescriptor());
+ assertEquals(device.isExternal(), outDevice.isExternal());
+ assertEquals(device.getSources(), outDevice.getSources());
+ assertEquals(device.getKeyboardType(), outDevice.getKeyboardType());
+ assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
+
+ KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
+ KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap();
+ assertTrue("keyCharacterMap not equal", keyCharacterMap.equals(outKeyCharacterMap));
+
+ for (int j = 0; j < device.getMotionRanges().size(); j++) {
+ assertMotionRangeEquals(device.getMotionRanges().get(j),
+ outDevice.getMotionRanges().get(j));
+ }
+ }
+
+ private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) {
+ final InputDevice device =
+ new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name",
+ 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+ 0 /* sources */, 0 /* keyboardType */, keyCharacterMap,
+ false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+ true /* hasSensor */, false /* hasBattery */);
+
+ Parcel parcel = Parcel.obtain();
+ device.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ InputDevice outDevice = InputDevice.CREATOR.createFromParcel(parcel);
+ assertDeviceEquals(device, outDevice);
+ }
+
+ @Test
+ public void testParcelUnparcelInputDevice_VirtualCharacterMap() {
+ final KeyCharacterMap keyCharacterMap =
+ KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ assertInputDeviceParcelUnparcel(keyCharacterMap);
+ }
+
+ @Test
+ public void testParcelUnparcelInputDevice_EmptyCharacterMap() {
+ final KeyCharacterMap keyCharacterMap = KeyCharacterMap.obtainEmptyMap(DEVICE_ID);
+ assertInputDeviceParcelUnparcel(keyCharacterMap);
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index 2e985fbba269..b9b347b02958 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -43,7 +43,7 @@ fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent {
xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
}
-fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
+private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
val code = KeyEvent.KEYCODE_A
val repeat = 0
return KeyEvent(eventTime, eventTime, action, code, repeat)
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
new file mode 100644
index 000000000000..4f95ce585de2
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.input
+
+import android.os.HandlerThread
+import android.os.Looper
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputEventSender
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.CountDownLatch
+import org.junit.Assert.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
+ assertEquals(expected.action, received.action)
+ assertEquals(expected.deviceId, received.deviceId)
+ assertEquals(expected.downTime, received.downTime)
+ assertEquals(expected.eventTime, received.eventTime)
+ assertEquals(expected.keyCode, received.keyCode)
+ assertEquals(expected.scanCode, received.scanCode)
+ assertEquals(expected.repeatCount, received.repeatCount)
+ assertEquals(expected.metaState, received.metaState)
+ assertEquals(expected.flags, received.flags)
+ assertEquals(expected.source, received.source)
+ assertEquals(expected.displayId, received.displayId)
+}
+
+class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+ InputEventReceiver(channel, looper) {
+ companion object {
+ const val TAG = "TestInputEventReceiver"
+ }
+
+ var lastEvent: InputEvent? = null
+
+ override fun onInputEvent(event: InputEvent) {
+ lastEvent = when (event) {
+ is KeyEvent -> KeyEvent.obtain(event)
+ is MotionEvent -> MotionEvent.obtain(event)
+ else -> throw Exception("Received $event is neither a key nor a motion")
+ }
+ finishInputEvent(event, true /*handled*/)
+ }
+}
+
+class TestInputEventSender(channel: InputChannel, looper: Looper) :
+ InputEventSender(channel, looper) {
+ companion object {
+ const val TAG = "TestInputEventSender"
+ }
+ data class FinishedResult(val seq: Int, val handled: Boolean)
+
+ private var mFinishedSignal = CountDownLatch(1)
+ override fun onInputEventFinished(seq: Int, handled: Boolean) {
+ finishedResult = FinishedResult(seq, handled)
+ mFinishedSignal.countDown()
+ }
+ lateinit var finishedResult: FinishedResult
+
+ fun waitForFinish() {
+ mFinishedSignal.await()
+ mFinishedSignal = CountDownLatch(1) // Ready for next event
+ }
+}
+
+class InputEventSenderAndReceiverTest {
+ companion object {
+ private const val TAG = "InputEventSenderAndReceiverTest"
+ }
+ private val mHandlerThread = HandlerThread("Process input events")
+ private lateinit var mReceiver: TestInputEventReceiver
+ private lateinit var mSender: TestInputEventSender
+
+ @Before
+ fun setUp() {
+ val channels = InputChannel.openInputChannelPair("TestChannel")
+ mHandlerThread.start()
+
+ val looper = mHandlerThread.getLooper()
+ mSender = TestInputEventSender(channels[0], looper)
+ mReceiver = TestInputEventReceiver(channels[1], looper)
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ }
+
+ @Test
+ fun testSendAndReceiveKey() {
+ val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0 /*repeat*/)
+ val seq = 10
+ mSender.sendInputEvent(seq, key)
+ mSender.waitForFinish()
+
+ // Check receiver
+ assertKeyEvent(key, mReceiver.lastEvent!! as KeyEvent)
+
+ // Check sender
+ assertEquals(seq, mSender.finishedResult.seq)
+ assertEquals(true, mSender.finishedResult.handled)
+ }
+}
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index d6846faa5c00..df58da5f157a 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -94,10 +94,7 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule {
if (platformCompat == null) {
throw new IllegalStateException("Could not get IPlatformCompat service!");
}
- uiAutomation.adoptShellPermissionIdentity(
- Manifest.permission.LOG_COMPAT_CHANGE,
- Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+ adoptShellPermissions(uiAutomation);
Compatibility.setOverrides(mConfig);
try {
platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
@@ -105,6 +102,7 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule {
try {
mTestStatement.evaluate();
} finally {
+ adoptShellPermissions(uiAutomation);
platformCompat.clearOverridesForTest(packageName);
}
} catch (RemoteException e) {
@@ -114,5 +112,12 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule {
Compatibility.clearOverrides();
}
}
+
+ private static void adoptShellPermissions(UiAutomation uiAutomation) {
+ uiAutomation.adoptShellPermissionIdentity(
+ Manifest.permission.LOG_COMPAT_CHANGE,
+ Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+ }
}
}
diff --git a/tests/RollbackTest/MultiUserRollbackTest.xml b/tests/RollbackTest/MultiUserRollbackTest.xml
index 2f62af1856da..8fa0510d9e71 100644
--- a/tests/RollbackTest/MultiUserRollbackTest.xml
+++ b/tests/RollbackTest/MultiUserRollbackTest.xml
@@ -20,6 +20,8 @@
<option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="run-command" value="setprop persist.rollback.is_test 1" />
+ <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.tests.rollback.host.MultiUserRollbackTest" />
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest.xml b/tests/RollbackTest/NetworkStagedRollbackTest.xml
index 2ab907a59298..13f603140aee 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest.xml
+++ b/tests/RollbackTest/NetworkStagedRollbackTest.xml
@@ -24,6 +24,8 @@
<option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\\*' --esa flags &quot;ModuleConfig__versioned_immediate_commit_packages&quot; --esa types &quot;bytes&quot; --esa values &quot;Cm5vdGFwYWNrYWdlOgA=&quot; com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__immediate_commit_packages&quot; com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__versioned_immediate_commit_packages&quot; com.google.android.gms" />
+ <option name="run-command" value="setprop persist.rollback.is_test 1" />
+ <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.tests.rollback.host.NetworkStagedRollbackTest" />
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 7b85cc84f1f5..fbb6e46e1721 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -27,6 +27,8 @@
<option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="run-command" value="setprop persist.rollback.is_test 1" />
+ <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/StagedRollbackTest.xml b/tests/RollbackTest/StagedRollbackTest.xml
index 83fef8e0a04b..0ca4dafae59d 100644
--- a/tests/RollbackTest/StagedRollbackTest.xml
+++ b/tests/RollbackTest/StagedRollbackTest.xml
@@ -24,6 +24,8 @@
<option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="run-command" value="setprop persist.rollback.is_test 1" />
+ <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.tests.rollback.host.StagedRollbackTest" />
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
index ce226fdce320..926ff4d5793c 100644
--- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -130,6 +130,9 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceDequeueBuffer(J
return result;
}
sBuffers[slot] = anb;
+ if (timeoutMs == 0) {
+ return android::OK;
+ }
android::sp<android::Fence> fence(new android::Fence(fenceFd));
int waitResult = fence->wait(timeoutMs);
if (waitResult != android::OK) {
@@ -197,6 +200,28 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceQueueBuffer(JNI
return result;
}
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetAsyncMode(JNIEnv* /* env */,
+ jclass /* clazz */,
+ jboolean async) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setAsyncMode(async);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetDequeueTimeout(
+ JNIEnv* /* env */, jclass /* clazz */, jlong timeoutMs) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setDequeueTimeout(timeoutMs);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetMaxDequeuedBufferCount(
+ JNIEnv* /* env */, jclass /* clazz */, jint maxDequeuedBuffers) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+}
+
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount(
JNIEnv* /* env */, jclass /* clazz */, jint count) {
assert(sAnw);
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
index 7d278dc8acc0..b67dc380efab 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -17,6 +17,7 @@ package com.android.test
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -93,4 +94,80 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
+
+ @Test
+ // Leave IGBP in sync mode, try to dequeue and queue as fast as possible. Check that we
+ // occasionally get timeout errors.
+ fun testSyncMode_dequeueWithoutBlockingFails() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ var failures = false
+ for (i in 1..numFrames) {
+ if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+ failures = true
+ break
+ }
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertTrue(failures)
+ }
+ }
+
+ @Test
+ // Set IGBP to be in async mode, try to dequeue and queue as fast as possible. Client should be
+ // able to dequeue and queue buffers without being blocked.
+ fun testAsyncMode_dequeueWithoutBlocking() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true))
+ for (i in 1..numFrames) {
+ assertEquals(0, activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */))
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ }
+ }
+
+ @Test
+ // Disable triple buffering in the system and leave IGBP in sync mode. Check that we
+ // occasionally get timeout errors.
+ fun testSyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ var failures = false
+ for (i in 1..numFrames) {
+ if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+ failures = true
+ break
+ }
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertTrue(failures)
+ }
+ }
+
+ @Test
+ // Disable triple buffering in the system and set IGBP to be in async mode. Try to dequeue and
+ // queue as fast as possible. Without triple buffering, the client does not have an extra buffer
+ // to dequeue and will not be able to dequeue and queue buffers without being blocked.
+ fun testAsyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() {
+ val numFrames = 1000L
+ runOnUiThread { activity ->
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+ assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true))
+ var failures = false
+ for (i in 1..numFrames) {
+ if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+ failures = true
+ break
+ }
+ activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertTrue(failures)
+ }
+ }
} \ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
index cfbd3ac11acb..45a70944204c 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -54,7 +54,13 @@ class SurfaceProxy {
external fun SurfaceSetScalingMode(scalingMode: Int)
external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int
external fun SurfaceCancelBuffer(slot: Int)
- external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true)
+ external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true): Int
+ external fun SurfaceSetAsyncMode(async: Boolean): Int
+ external fun SurfaceSetDequeueTimeout(timeout: Long): Int
+ external fun SurfaceQuery(what: Int): Int
+ external fun SurfaceSetMaxDequeuedBufferCount(maxDequeuedBuffers: Int): Int
+
+ // system/native_window.h functions
external fun NativeWindowSetBufferCount(count: Int): Int
external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int
external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 7f0318a135dc..e1a424f214a5 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -79,6 +79,7 @@ android_test {
"android.test.runner",
"android.test.base",
"android.test.mock",
+ "ServiceConnectivityResources",
],
jni_libs: [
"libservice-connectivity",
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index 89fc6ea2c47b..d659688700d3 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -9,6 +9,23 @@
"name": "FrameworksNetDeflakeTest"
}
],
+ "auto-postsubmit": [
+ // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+ // automotive targets to avoid introducing additional test flake and build time. The plan for
+ // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+ // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+ // infra during the hardening phase.
+ // TODO: this tag to be removed once the above is no longer an issue.
+ {
+ "name": "FrameworksNetTests"
+ },
+ {
+ "name": "FrameworksNetIntegrationTests"
+ },
+ {
+ "name": "FrameworksNetDeflakeTest"
+ }
+ ],
"imports": [
{
"path": "cts/tests/tests/net"
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java
index 9c0fc7ce7881..50ecb428359e 100644
--- a/tests/net/common/java/android/net/IpPrefixTest.java
+++ b/tests/net/common/java/android/net/IpPrefixTest.java
@@ -113,6 +113,15 @@ public class IpPrefixTest {
p = new IpPrefix("f00:::/32");
fail("Expected IllegalArgumentException: invalid IPv6 address");
} catch (IllegalArgumentException expected) { }
+
+ p = new IpPrefix("/64");
+ assertEquals("::/64", p.toString());
+
+ p = new IpPrefix("/128");
+ assertEquals("::1/128", p.toString());
+
+ p = new IpPrefix("[2001:db8::123]/64");
+ assertEquals("2001:db8::/64", p.toString());
}
@Test
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 1eaf30c5e068..2cf3cf9c11da 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -53,6 +53,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
@@ -117,6 +118,20 @@ public class LinkAddressTest {
assertEquals(456, address.getScope());
assertTrue(address.isIpv4());
+ address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */);
+ assertEquals(Inet6Address.LOOPBACK, address.getAddress());
+ assertEquals(64, address.getPrefixLength());
+ assertEquals(1, address.getFlags());
+ assertEquals(2, address.getScope());
+ assertTrue(address.isIpv6());
+
+ address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */);
+ assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress());
+ assertEquals(64, address.getPrefixLength());
+ assertEquals(3, address.getFlags());
+ assertEquals(4, address.getScope());
+ assertTrue(address.isIpv6());
+
// InterfaceAddress doesn't have a constructor. Fetch some from an interface.
List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index ddc31bfc20b8..0dfec7592274 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -28,6 +28,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
@@ -44,6 +45,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
import static android.os.Process.INVALID_UID;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.testutils.MiscAsserts.assertEmpty;
+import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -67,7 +72,7 @@ import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
-import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.CompatUtil;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -84,6 +89,9 @@ import java.util.Set;
public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+ private static final int TEST_SUBID1 = 1;
+ private static final int TEST_SUBID2 = 2;
+ private static final int TEST_SUBID3 = 3;
@Rule
public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
@@ -91,14 +99,6 @@ public class NetworkCapabilitiesTest {
private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
- private boolean isAtLeastR() {
- return SdkLevel.isAtLeastR();
- }
-
- private boolean isAtLeastS() {
- return SdkLevel.isAtLeastS();
- }
-
@Test
public void testMaybeMarkCapabilitiesRestricted() {
// verify EIMS is restricted
@@ -211,7 +211,7 @@ public class NetworkCapabilitiesTest {
nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
nc2 = new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
- .setNetworkSpecifier(new EthernetNetworkSpecifier("eth42"));
+ .setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth42"));
assertNotEquals("", nc1.describeImmutableDifferences(nc2));
assertEquals("", nc1.describeImmutableDifferences(nc1));
}
@@ -304,7 +304,9 @@ public class NetworkCapabilitiesTest {
.setUids(uids)
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
- if (isAtLeastR()) {
+ if (isAtLeastS()) {
+ netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+ } else if (isAtLeastR()) {
netCap.setOwnerUid(123);
netCap.setAdministratorUids(new int[] {5, 11});
}
@@ -379,7 +381,7 @@ public class NetworkCapabilitiesTest {
private void testParcelSane(NetworkCapabilities cap) {
if (isAtLeastS()) {
- assertParcelSane(cap, 16);
+ assertParcelSane(cap, 17);
} else if (isAtLeastR()) {
assertParcelSane(cap, 15);
} else {
@@ -613,6 +615,20 @@ public class NetworkCapabilitiesTest {
assertFalse(nc2.appliesToUid(12));
assertTrue(nc1.appliesToUid(22));
assertTrue(nc2.appliesToUid(22));
+
+ // Verify the subscription id list can be combined only when they are equal.
+ if (isAtLeastS()) {
+ nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+ nc2.setSubIds(Set.of(TEST_SUBID2));
+ assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+ nc2.setSubIds(Set.of());
+ assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+ nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+ nc2.combineCapabilities(nc1);
+ assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+ }
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -671,7 +687,7 @@ public class NetworkCapabilitiesTest {
NetworkCapabilities nc1 = new NetworkCapabilities();
nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI);
try {
- nc1.setNetworkSpecifier(new EthernetNetworkSpecifier("eth0"));
+ nc1.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth0"));
fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
} catch (IllegalStateException expected) {
// empty
@@ -680,7 +696,7 @@ public class NetworkCapabilitiesTest {
// Sequence 2: Transport + NetworkSpecifier + Transport
NetworkCapabilities nc2 = new NetworkCapabilities();
nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier(
- new EthernetNetworkSpecifier("testtap3"));
+ CompatUtil.makeEthernetNetworkSpecifier("testtap3"));
try {
nc2.addTransportType(TRANSPORT_WIFI);
fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
@@ -761,6 +777,24 @@ public class NetworkCapabilitiesTest {
nc1.setUids(uidRange(10, 13));
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities
assertEquals(nc1, nc2);
+
+ if (isAtLeastS()) {
+ assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
+ nc1.setSubIds(Set.of());
+ nc2.set(nc1);
+ assertEquals(nc1, nc2);
+
+ nc1.setSubIds(Set.of(TEST_SUBID1));
+ nc2.set(nc1);
+ assertEquals(nc1, nc2);
+
+ nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+ nc2.set(nc1);
+ assertEquals(nc1, nc2);
+
+ nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+ assertNotEquals(nc1, nc2);
+ }
}
@Test
@@ -841,6 +875,50 @@ public class NetworkCapabilitiesTest {
} catch (NullPointerException expected) { }
}
+ private static NetworkCapabilities capsWithSubIds(Integer ... subIds) {
+ // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for
+ // every NetworkCapabilities that simulates networks needs to add it too in order to
+ // satisfy these requests.
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .setSubIds(new ArraySet<>(subIds)).build();
+ assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+ return nc;
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testSubIds() throws Exception {
+ final NetworkCapabilities ncWithoutId = capsWithSubIds();
+ final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1);
+ final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3);
+ final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
+
+ final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
+ assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+ final NetworkRequest requestWithIds = new NetworkRequest.Builder()
+ .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+ assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
+ requestWithIds.networkCapabilities.getSubIds());
+
+ assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
+ assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
+ assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds));
+ assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId));
+ assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId));
+ assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId));
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testEqualsSubIds() throws Exception {
+ assertEquals(capsWithSubIds(), capsWithSubIds());
+ assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1));
+ assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1));
+ assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2));
+ assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+ assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2),
+ capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+ }
+
@Test
public void testLinkBandwidthKbps() {
final NetworkCapabilities nc = new NetworkCapabilities();
@@ -1021,5 +1099,11 @@ public class NetworkCapabilitiesTest {
fail("Should not set null into NetworkCapabilities.Builder");
} catch (NullPointerException expected) { }
assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+
+ if (isAtLeastS()) {
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setSubIds(Set.of(TEST_SUBID1)).build();
+ assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+ }
}
}
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index 71a7a7c4ebd9..340e6f963137 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -27,6 +27,7 @@ import android.os.HandlerThread
import android.os.Looper
import androidx.test.InstrumentationRegistry
import com.android.net.module.util.ArrayTrackRecord
+import com.android.testutils.CompatUtil
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.isDevSdkInRange
@@ -102,7 +103,8 @@ class NetworkProviderTest {
mCm.registerNetworkProvider(provider)
assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
- val specifier = EthernetNetworkSpecifier(UUID.randomUUID().toString())
+ val specifier = CompatUtil.makeTestNetworkSpecifier(
+ UUID.randomUUID().toString())
val nr: NetworkRequest = NetworkRequest.Builder()
.addTransportType(TRANSPORT_TEST)
.setNetworkSpecifier(specifier)
@@ -183,7 +185,8 @@ class NetworkProviderTest {
mCm.registerNetworkProvider(provider)
- val specifier = EthernetNetworkSpecifier(UUID.randomUUID().toString())
+ val specifier = CompatUtil.makeTestNetworkSpecifier(
+ UUID.randomUUID().toString())
val nr: NetworkRequest = NetworkRequest.Builder()
.addTransportType(TRANSPORT_TEST)
.setNetworkSpecifier(specifier)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index e1da3d0ae2b3..01d8186c7d1b 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -64,6 +64,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
private final HandlerThread mHandlerThread;
private final Context mContext;
private final String mLogTag;
+ private final NetworkAgentConfig mNetworkAgentConfig;
private final ConditionVariable mDisconnected = new ConditionVariable();
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
@@ -115,13 +116,19 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
mHandlerThread = new HandlerThread(mLogTag);
mHandlerThread.start();
- mNetworkAgent = makeNetworkAgent(linkProperties, type, typeName);
+ // extraInfo is set to "" by default in NetworkAgentConfig.
+ final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : "";
+ mNetworkAgentConfig = new NetworkAgentConfig.Builder()
+ .setLegacyType(type)
+ .setLegacyTypeName(typeName)
+ .setLegacyExtraInfo(extraInfo)
+ .build();
+ mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig);
}
protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
- final int type, final String typeName)
- throws Exception {
- return new InstrumentedNetworkAgent(this, linkProperties, type, typeName);
+ final NetworkAgentConfig nac) throws Exception {
+ return new InstrumentedNetworkAgent(this, linkProperties, nac);
}
public static class InstrumentedNetworkAgent extends NetworkAgent {
@@ -129,11 +136,9 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
private static final String PROVIDER_NAME = "InstrumentedNetworkAgentProvider";
public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
- final int type, final String typeName) {
+ NetworkAgentConfig nac) {
super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag,
- wrapper.mNetworkCapabilities, lp, wrapper.mScore,
- new NetworkAgentConfig.Builder()
- .setLegacyType(type).setLegacyTypeName(typeName).build(),
+ wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac,
new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(),
PROVIDER_NAME));
mWrapper = wrapper;
@@ -301,6 +306,14 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
return mNetworkCapabilities;
}
+ public int getLegacyType() {
+ return mNetworkAgentConfig.getLegacyType();
+ }
+
+ public String getExtraInfo() {
+ return mNetworkAgentConfig.getLegacyExtraInfo();
+ }
+
public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() {
return mCallbackHistory;
}
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 6a09b0237a38..6fc605e269fe 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -220,7 +220,7 @@ public class ConnectivityManagerTest {
// register callback
when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
- any(), nullable(String.class))).thenReturn(request);
+ anyInt(), any(), nullable(String.class))).thenReturn(request);
manager.requestNetwork(request, callback, handler);
// callback triggers
@@ -248,7 +248,7 @@ public class ConnectivityManagerTest {
// register callback
when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
- any(), nullable(String.class))).thenReturn(req1);
+ anyInt(), any(), nullable(String.class))).thenReturn(req1);
manager.requestNetwork(req1, callback, handler);
// callback triggers
@@ -266,7 +266,7 @@ public class ConnectivityManagerTest {
// callback can be registered again
when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
- any(), nullable(String.class))).thenReturn(req2);
+ anyInt(), any(), nullable(String.class))).thenReturn(req2);
manager.requestNetwork(req2, callback, handler);
// callback triggers
@@ -289,8 +289,8 @@ public class ConnectivityManagerTest {
info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
when(mCtx.getApplicationInfo()).thenReturn(info);
- when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(),
- nullable(String.class))).thenReturn(request);
+ when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(),
+ any(), nullable(String.class))).thenReturn(request);
Handler handler = new Handler(Looper.getMainLooper());
manager.requestNetwork(request, callback, handler);
@@ -358,34 +358,34 @@ public class ConnectivityManagerTest {
manager.requestNetwork(request, callback);
verify(mService).requestNetwork(eq(request.networkCapabilities),
- eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+ eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
eq(testPkgName), eq(testAttributionTag));
reset(mService);
// Verify that register network callback does not calls requestNetwork at all.
manager.registerNetworkCallback(request, callback);
- verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
+ verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(),
anyInt(), any(), any());
- verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
+ verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
eq(testPkgName), eq(testAttributionTag));
reset(mService);
manager.registerDefaultNetworkCallback(callback);
verify(mService).requestNetwork(eq(null),
- eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+ eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
eq(testPkgName), eq(testAttributionTag));
reset(mService);
- manager.requestBackgroundNetwork(request, null, callback);
+ Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+ manager.requestBackgroundNetwork(request, handler, callback);
verify(mService).requestNetwork(eq(request.networkCapabilities),
- eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+ eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
eq(testPkgName), eq(testAttributionTag));
reset(mService);
- Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
manager.registerSystemDefaultNetworkCallback(callback, handler);
verify(mService).requestNetwork(eq(null),
- eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+ eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
eq(testPkgName), eq(testAttributionTag));
reset(mService);
}
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index 8ea226db938e..b62bdbcfb5eb 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -18,6 +18,7 @@ package android.net.util
import android.content.Context
import android.content.res.Resources
+import android.net.ConnectivityResources
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.MAX_TRANSPORT
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
@@ -26,13 +27,15 @@ import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import androidx.test.filters.SmallTest
import com.android.internal.R
+import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.any
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
@@ -47,21 +50,33 @@ import org.mockito.Mockito.mock
class KeepaliveUtilsTest {
// Prepare mocked context with given resource strings.
- private fun getMockedContextWithStringArrayRes(id: Int, res: Array<out String?>?): Context {
+ private fun getMockedContextWithStringArrayRes(
+ id: Int,
+ name: String,
+ res: Array<out String?>?
+ ): Context {
val mockRes = mock(Resources::class.java)
- doReturn(res).`when`(mockRes).getStringArray(ArgumentMatchers.eq(id))
+ doReturn(res).`when`(mockRes).getStringArray(eq(id))
+ doReturn(id).`when`(mockRes).getIdentifier(eq(name), any(), any())
return mock(Context::class.java).apply {
doReturn(mockRes).`when`(this).getResources()
+ ConnectivityResources.setResourcesContextForTest(this)
}
}
+ @After
+ fun tearDown() {
+ ConnectivityResources.setResourcesContextForTest(null)
+ }
+
@Test
fun testGetSupportedKeepalives() {
fun assertRunWithException(res: Array<out String?>?) {
try {
val mockContext = getMockedContextWithStringArrayRes(
- R.array.config_networkSupportedKeepaliveCount, res)
+ R.array.config_networkSupportedKeepaliveCount,
+ "config_networkSupportedKeepaliveCount", res)
KeepaliveUtils.getSupportedKeepalives(mockContext)
fail("Expected KeepaliveDeviceConfigurationException")
} catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) {
@@ -89,7 +104,8 @@ class KeepaliveUtilsTest {
val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
val mockContext = getMockedContextWithStringArrayRes(
- R.array.config_networkSupportedKeepaliveCount, validRes)
+ R.array.config_networkSupportedKeepaliveCount,
+ "config_networkSupportedKeepaliveCount", validRes)
val actual = KeepaliveUtils.getSupportedKeepalives(mockContext)
assertArrayEquals(expectedValidRes, actual)
}
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index c1315f64c56b..789f2d053c6c 100644
--- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -21,6 +21,7 @@ import android.content.res.Resources
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
+import android.net.ConnectivityResources
import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
import android.provider.Settings
import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
@@ -31,8 +32,9 @@ import android.telephony.TelephonyManager
import android.test.mock.MockContentResolver
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.R
+import com.android.connectivity.resources.R
import com.android.internal.util.test.FakeSettingsProvider
+import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -41,6 +43,7 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.any
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
@@ -57,6 +60,8 @@ import org.mockito.Mockito.verify
@SmallTest
class MultinetworkPolicyTrackerTest {
private val resources = mock(Resources::class.java).also {
+ doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier(
+ eq("config_networkAvoidBadWifi"), eq("integer"), any())
doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
}
private val telephonyManager = mock(TelephonyManager::class.java)
@@ -75,6 +80,7 @@ class MultinetworkPolicyTrackerTest {
doReturn(resources).`when`(it).resources
doReturn(it).`when`(it).createConfigurationContext(any())
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
+ ConnectivityResources.setResourcesContextForTest(it)
}
private val tracker = MultinetworkPolicyTracker(context, null /* handler */)
@@ -85,6 +91,11 @@ class MultinetworkPolicyTrackerTest {
assertEquals(preference, tracker.meteredMultipathPreference)
}
+ @After
+ fun tearDown() {
+ ConnectivityResources.setResourcesContextForTest(null)
+ }
+
@Test
fun testUpdateMeteredMultipathPreference() {
assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2c6a21ad7570..fd49d0575e03 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,8 @@ import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -35,11 +37,14 @@ import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
@@ -82,10 +87,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.RULE_NONE;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
@@ -174,6 +179,7 @@ import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
+import android.net.ConnectivityResources;
import android.net.ConnectivityThread;
import android.net.DataStallReportParcelable;
import android.net.EthernetManager;
@@ -182,8 +188,7 @@ import android.net.IDnsResolver;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
-import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
import android.net.IQosCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -201,7 +206,9 @@ import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicyManager;
+import android.net.NetworkPolicyManager.NetworkPolicyCallback;
import android.net.NetworkRequest;
+import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
@@ -266,7 +273,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
@@ -377,6 +383,11 @@ public class ConnectivityServiceTest {
// Set a non-zero value to verify the flow to set tcp init rwnd value.
private static final int TEST_TCP_INIT_RWND = 60;
+ // Used for testing the per-work-profile default network.
+ private static final int TEST_APP_ID = 103;
+ private static final int TEST_WORK_PROFILE_USER_ID = 2;
+ private static final int TEST_WORK_PROFILE_APP_UID =
+ UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -411,7 +422,7 @@ public class ConnectivityServiceTest {
private TestNetworkAgentWrapper mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
- private INetworkPolicyListener mPolicyListener;
+ private NetworkPolicyCallback mPolicyCallback;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
@@ -420,15 +431,14 @@ public class ConnectivityServiceTest {
private VpnManagerService mVpnManagerService;
private TestNetworkCallback mDefaultNetworkCallback;
private TestNetworkCallback mSystemDefaultNetworkCallback;
+ private TestNetworkCallback mProfileDefaultNetworkCallback;
// State variables required to emulate NetworkPolicyManagerService behaviour.
- private int mUidRules = RULE_NONE;
- private boolean mRestrictBackground = false;
+ private int mBlockedReasons = BLOCKED_REASON_NONE;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@Mock NetworkStatsManager mStatsManager;
- @Mock IBatteryStats mBatteryStatsService;
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@Mock NetworkStackClient mNetworkStack;
@@ -446,6 +456,7 @@ public class ConnectivityServiceTest {
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
+ @Mock Resources mResources;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -473,7 +484,7 @@ public class ConnectivityServiceTest {
private class MockContext extends BroadcastInterceptingContext {
private final MockContentResolver mContentResolver;
- @Spy private Resources mResources;
+ @Spy private Resources mInternalResources;
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
// Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
@@ -482,21 +493,15 @@ public class ConnectivityServiceTest {
MockContext(Context base, ContentProvider settingsProvider) {
super(base);
- mResources = spy(base.getResources());
- when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)).
- thenReturn(new String[] {
+ mInternalResources = spy(base.getResources());
+ when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
+ .thenReturn(new String[] {
"wifi,1,1,1,-1,true",
"mobile,0,0,0,-1,true",
"mobile_mms,2,0,2,60000,true",
"mobile_supl,3,0,2,60000,true",
});
- when(mResources.getStringArray(
- com.android.internal.R.array.config_wakeonlan_supported_interfaces))
- .thenReturn(new String[]{
- WIFI_WOL_IFNAME,
- });
-
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
}
@@ -547,13 +552,26 @@ public class ConnectivityServiceTest {
return super.getSystemService(name);
}
+ final HashMap<UserHandle, UserManager> mUserManagers = new HashMap<>();
@Override
public Context createContextAsUser(UserHandle user, int flags) {
final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this));
doReturn(user).when(asUser).getUser();
+ doAnswer((inv) -> {
+ final UserManager um = mUserManagers.computeIfAbsent(user,
+ u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager)));
+ return um;
+ }).when(asUser).getSystemService(Context.USER_SERVICE);
return asUser;
}
+ public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) {
+ // This relies on all contexts for a given user returning the same UM mock
+ final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
+ .getSystemService(UserManager.class);
+ doReturn(value).when(umMock).isManagedProfile();
+ }
+
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
@@ -561,7 +579,7 @@ public class ConnectivityServiceTest {
@Override
public Resources getResources() {
- return mResources;
+ return mInternalResources;
}
@Override
@@ -720,7 +738,7 @@ public class ConnectivityServiceTest {
@Override
protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
- final int type, final String typeName) throws Exception {
+ NetworkAgentConfig nac) throws Exception {
mNetworkMonitor = mock(INetworkMonitor.class);
final Answer validateAnswer = inv -> {
@@ -739,8 +757,8 @@ public class ConnectivityServiceTest {
any() /* name */,
nmCbCaptor.capture());
- final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties,
- type, typeName) {
+ final InstrumentedNetworkAgent na =
+ new InstrumentedNetworkAgent(this, linkProperties, nac) {
@Override
public void networkStatus(int status, String redirectUrl) {
mRedirectUrl = redirectUrl;
@@ -1084,6 +1102,10 @@ public class ConnectivityServiceTest {
public void triggerUnfulfillable(NetworkRequest r) {
super.releaseRequestAsUnfulfillableByAnyFactory(r);
}
+
+ public void assertNoRequestChanged() {
+ assertNull(mRequestHistory.poll(0, r -> true));
+ }
}
private Set<UidRange> uidRangesForUids(int... uids) {
@@ -1351,28 +1373,13 @@ public class ConnectivityServiceTest {
}
private void mockUidNetworkingBlocked() {
- doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
- .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
- i.getArgument(1) /* metered */, mRestrictBackground)
+ doAnswer(i -> NetworkPolicyManager.isUidBlocked(mBlockedReasons, i.getArgument(1))
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
-
- doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
- .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
- inv.getArgument(1) /* uidRules */,
- inv.getArgument(2) /* isNetworkMetered */,
- inv.getArgument(3) /* isBackgroundRestricted */)
- ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
- anyInt(), anyInt(), anyBoolean(), anyBoolean());
}
- private void setUidRulesChanged(int uidRules) throws RemoteException {
- mUidRules = uidRules;
- mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
- }
-
- private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
- mRestrictBackground = restrictBackground;
- mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
+ private void setBlockedReasonChanged(int blockedReasons) {
+ mBlockedReasons = blockedReasons;
+ mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons);
}
private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
@@ -1409,17 +1416,36 @@ public class ConnectivityServiceTest {
fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
- private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
- int uid) {
+ private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
when(mDeps.getCallingUid()).thenReturn(uid);
try {
- mCm.registerNetworkCallback(request, callback);
- waitForIdle();
+ return what.get();
} finally {
returnRealCallingUid();
}
}
+ private void doAsUid(final int uid, @NonNull final Runnable what) {
+ doAsUid(uid, () -> {
+ what.run(); return Void.TYPE;
+ });
+ }
+
+ private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
+ int uid) {
+ doAsUid(uid, () -> {
+ mCm.registerNetworkCallback(request, callback);
+ });
+ }
+
+ private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback,
+ final int uid) {
+ doAsUid(uid, () -> {
+ mCm.registerDefaultNetworkCallback(callback);
+ waitForIdle();
+ });
+ }
+
private static final int PRIMARY_USER = 0;
private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
@@ -1456,6 +1482,8 @@ public class ConnectivityServiceTest {
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenReturn(applicationInfo);
+ when(mPackageManager.getTargetSdkVersion(anyString()))
+ .thenReturn(applicationInfo.targetSdkVersion);
when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
@@ -1464,6 +1492,9 @@ public class ConnectivityServiceTest {
Looper.prepare();
}
mockDefaultPackages();
+ mockHasSystemFeature(FEATURE_WIFI, true);
+ mockHasSystemFeature(FEATURE_WIFI_DIRECT, true);
+ doReturn(true).when(mTelephonyManager).isDataCapable();
FakeSettingsProvider.clearSettingsProvider();
mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
@@ -1490,10 +1521,11 @@ public class ConnectivityServiceTest {
mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
- final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
- ArgumentCaptor.forClass(INetworkPolicyListener.class);
- verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
- mPolicyListener = policyListenerCaptor.getValue();
+ final ArgumentCaptor<NetworkPolicyCallback> policyCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkPolicyCallback.class);
+ verify(mNetworkPolicyManager).registerNetworkPolicyCallback(any(),
+ policyCallbackCaptor.capture());
+ mPolicyCallback = policyCallbackCaptor.getValue();
// Create local CM before sending system ready so that we can answer
// getSystemService() correctly.
@@ -1516,10 +1548,7 @@ public class ConnectivityServiceTest {
}
private ConnectivityService.Dependencies makeDependencies() {
- doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
- .getInt("net.tcp.default_init_rwnd", 0);
doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
- doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
doReturn(mNetIdManager).when(deps).makeNetIdManager();
@@ -1527,12 +1556,31 @@ public class ConnectivityServiceTest {
doReturn(mSystemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
- doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
return mPolicyTracker;
}).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+ doReturn(true).when(deps).getCellular464XlatEnabled();
+
+ doReturn(60000).when(mResources).getInteger(
+ com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+ doReturn("").when(mResources).getString(
+ com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+ doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
+ com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces);
+ doReturn(new String[] { "0,1", "1,3" }).when(mResources).getStringArray(
+ com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount);
+ doReturn(com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount)
+ .when(mResources).getIdentifier(eq("config_networkSupportedKeepaliveCount"),
+ eq("array"), any());
+ final ConnectivityResources connRes = mock(ConnectivityResources.class);
+ doReturn(mResources).when(connRes).get();
+ doReturn(connRes).when(deps).getResources(any());
+
+ final Context mockResContext = mock(Context.class);
+ doReturn(mResources).when(mockResContext).getResources();
+ ConnectivityResources.setResourcesContextForTest(mockResContext);
return deps;
}
@@ -1566,6 +1614,7 @@ public class ConnectivityServiceTest {
@After
public void tearDown() throws Exception {
unregisterDefaultNetworkCallbacks();
+ maybeTearDownEnterpriseNetwork();
setAlwaysOnNetworks(false);
if (mCellNetworkAgent != null) {
mCellNetworkAgent.disconnect();
@@ -1588,6 +1637,7 @@ public class ConnectivityServiceTest {
waitForIdle();
FakeSettingsProvider.clearSettingsProvider();
+ ConnectivityResources.setResourcesContextForTest(null);
mCsHandlerThread.quitSafely();
mAlarmManagerThread.quitSafely();
@@ -1743,11 +1793,29 @@ public class ConnectivityServiceTest {
return expected;
}
+ private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) {
+ final DetailedState state = ni.getDetailedState();
+ if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false;
+ // Expect a null extraInfo if the network is CONNECTING, because a CONNECTIVITY_ACTION
+ // broadcast with a state of CONNECTING only happens due to legacy VPN lockdown, which also
+ // nulls out extraInfo.
+ if (state == DetailedState.CONNECTING && ni.getExtraInfo() != null) return false;
+ // Can't make any assertions about DISCONNECTED broadcasts. When a network actually
+ // disconnects, disconnectAndDestroyNetwork sets its state to DISCONNECTED and its extraInfo
+ // to null. But if the DISCONNECTED broadcast is just simulated by LegacyTypeTracker due to
+ // a network switch, extraInfo will likely be populated.
+ // This is likely a bug in CS, but likely not one we can fix without impacting apps.
+ return true;
+ }
+
private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
- return registerConnectivityBroadcastThat(1, intent ->
- type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
- ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
- .getDetailedState()));
+ return registerConnectivityBroadcastThat(1, intent -> {
+ final int actualType = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+ final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+ return type == actualType
+ && state == ni.getDetailedState()
+ && extraInfoInBroadcastHasExpectedNullness(ni);
+ });
}
@Test
@@ -1758,7 +1826,8 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
- assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+ assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+ assertFalse(mCm.isNetworkSupported(TYPE_PROXY));
// Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
// mocks, this assert exercises the ConnectivityService code path that ensures that
@@ -3733,8 +3802,8 @@ public class ConnectivityServiceTest {
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(),
- null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(),
- getAttributionTag());
+ null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE,
+ mContext.getPackageName(), getAttributionTag());
});
class NonParcelableSpecifier extends NetworkSpecifier {
@@ -4011,7 +4080,8 @@ public class ConnectivityServiceTest {
grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
- .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+ .addTransportType(TRANSPORT_CELLULAR).build(),
+ mCsHandlerThread.getThreadHandler(), cellBgCallback);
// Make callbacks for monitoring.
final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -7185,28 +7255,32 @@ public class ConnectivityServiceTest {
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
- setUidRulesChanged(RULE_REJECT_ALL);
+ setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertExtraInfoFromCmBlocked(mCellNetworkAgent);
// ConnectivityService should cache it not to invoke the callback again.
- setUidRulesChanged(RULE_REJECT_METERED);
+ setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED);
cellNetworkCallback.assertNoCallback();
- setUidRulesChanged(RULE_NONE);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
- setUidRulesChanged(RULE_REJECT_METERED);
+ setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertExtraInfoFromCmBlocked(mCellNetworkAgent);
// Restrict the network based on UID rule and NOT_METERED capability change.
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -7215,6 +7289,7 @@ public class ConnectivityServiceTest {
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
@@ -7223,35 +7298,40 @@ public class ConnectivityServiceTest {
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertExtraInfoFromCmBlocked(mCellNetworkAgent);
- setUidRulesChanged(RULE_ALLOW_METERED);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
- setUidRulesChanged(RULE_NONE);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.assertNoCallback();
// Restrict background data. Networking is not blocked because the network is unmetered.
- setRestrictBackgroundChanged(true);
+ setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
- setRestrictBackgroundChanged(true);
+ assertExtraInfoFromCmBlocked(mCellNetworkAgent);
+ setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.assertNoCallback();
- setUidRulesChanged(RULE_ALLOW_METERED);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
- setRestrictBackgroundChanged(false);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
mCm.unregisterNetworkCallback(cellNetworkCallback);
}
@@ -7263,9 +7343,9 @@ public class ConnectivityServiceTest {
mockUidNetworkingBlocked();
// No Networkcallbacks invoked before any network is active.
- setUidRulesChanged(RULE_REJECT_ALL);
- setUidRulesChanged(RULE_NONE);
- setUidRulesChanged(RULE_REJECT_METERED);
+ setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
+ setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
defaultCallback.assertNoCallback();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -7290,8 +7370,8 @@ public class ConnectivityServiceTest {
defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
// Verify there's no Networkcallbacks invoked after data saver on/off.
- setRestrictBackgroundChanged(true);
- setRestrictBackgroundChanged(false);
+ setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
+ setBlockedReasonChanged(BLOCKED_REASON_NONE);
defaultCallback.assertNoCallback();
mCellNetworkAgent.disconnect();
@@ -7310,6 +7390,15 @@ public class ConnectivityServiceTest {
assertNotNull(ni);
assertEquals(type, ni.getType());
assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+ if (state == DetailedState.CONNECTED || state == DetailedState.SUSPENDED) {
+ assertNotNull(ni.getExtraInfo());
+ } else {
+ // Technically speaking, a network that's in CONNECTING state will generally have a
+ // non-null extraInfo. This doesn't actually happen in this test because it never calls
+ // a legacy API while a network is connecting. When a network is in CONNECTING state
+ // because of legacy lockdown VPN, its extraInfo is always null.
+ assertNull(ni.getExtraInfo());
+ }
}
private void assertActiveNetworkInfo(int type, DetailedState state) {
@@ -7319,6 +7408,26 @@ public class ConnectivityServiceTest {
checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
}
+ private void assertExtraInfoFromCm(TestNetworkAgentWrapper network, boolean present) {
+ final NetworkInfo niForNetwork = mCm.getNetworkInfo(network.getNetwork());
+ final NetworkInfo niForType = mCm.getNetworkInfo(network.getLegacyType());
+ if (present) {
+ assertEquals(network.getExtraInfo(), niForNetwork.getExtraInfo());
+ assertEquals(network.getExtraInfo(), niForType.getExtraInfo());
+ } else {
+ assertNull(niForNetwork.getExtraInfo());
+ assertNull(niForType.getExtraInfo());
+ }
+ }
+
+ private void assertExtraInfoFromCmBlocked(TestNetworkAgentWrapper network) {
+ assertExtraInfoFromCm(network, false);
+ }
+
+ private void assertExtraInfoFromCmPresent(TestNetworkAgentWrapper network) {
+ assertExtraInfoFromCm(network, true);
+ }
+
// Checks that each of the |agents| receive a blocked status change callback with the specified
// |blocked| value, in any order. This is needed because when an event affects multiple
// networks, ConnectivityService does not guarantee the order in which callbacks are fired.
@@ -7633,6 +7742,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+ assertExtraInfoFromCmBlocked(mCellNetworkAgent);
// TODO: it would be nice if we could simply rely on the production code here, and have
// LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
@@ -7661,6 +7771,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mCellNetworkAgent);
assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7703,6 +7814,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+ assertExtraInfoFromCmBlocked(mWiFiNetworkAgent);
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
@@ -7717,6 +7829,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7733,6 +7846,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.disconnect();
@@ -7812,7 +7926,6 @@ public class ConnectivityServiceTest {
verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
cellLp.getInterfaceName(),
new int[] { TRANSPORT_CELLULAR });
- reset(mBatteryStatsService);
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wifi0");
@@ -7822,7 +7935,6 @@ public class ConnectivityServiceTest {
verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
wifiLp.getInterfaceName(),
new int[] { TRANSPORT_WIFI });
- reset(mBatteryStatsService);
mCellNetworkAgent.disconnect();
mWiFiNetworkAgent.disconnect();
@@ -7905,7 +8017,6 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
reset(mMockDnsResolver);
reset(mMockNetd);
- reset(mBatteryStatsService);
// Connect with ipv6 link properties. Expect prefix discovery to be started.
mCellNetworkAgent.connect(true);
@@ -7925,7 +8036,6 @@ public class ConnectivityServiceTest {
// Switching default network updates TCP buffer sizes.
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
- verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
// Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
// the NAT64 prefix was removed because one was never discovered.
cellLp.addLinkAddress(myIpv4);
@@ -8268,6 +8378,45 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testWith464XlatDisable() throws Exception {
+ doReturn(false).when(mDeps).getCellular464XlatEnabled();
+
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ mCm.registerNetworkCallback(networkRequest, callback);
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ // Bring up validated cell.
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+ cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, MOBILE_IFNAME));
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+ waitForIdle();
+
+ verify(mMockDnsResolver, never()).startPrefix64Discovery(cellNetId);
+ Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
+ assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+
+ // This cannot happen because prefix discovery cannot succeed if it is never started.
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+ makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, "64:ff9b::", 96));
+
+ // ... but still, check that even if it did, clatd would not be started.
+ verify(mMockNetd, never()).clatdStart(anyString(), anyString());
+ assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+ }
+
+ @Test
public void testDataActivityTracking() throws Exception {
final TestNetworkCallback networkCallback = new TestNetworkCallback();
final NetworkRequest networkRequest = new NetworkRequest.Builder()
@@ -8372,14 +8521,12 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false);
networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
- verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
// Change link Properties should have updated tcp buffer size.
LinkProperties lp = new LinkProperties();
lp.setTcpBufferSizes(testTcpBufferSizes);
mCellNetworkAgent.sendLinkProperties(lp);
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verifyTcpBufferSizeChange(testTcpBufferSizes);
- verify(mSystemProperties, times(2)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
// Clean up.
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -8659,6 +8806,7 @@ public class ConnectivityServiceTest {
applicationInfo.targetSdkVersion = targetSdk;
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
.thenReturn(applicationInfo);
+ when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
@@ -8673,102 +8821,183 @@ public class ConnectivityServiceTest {
}
}
- private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+ private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid,
+ boolean includeLocationSensitiveInfo) {
final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
+ netCap, includeLocationSensitiveInfo, callerUid,
+ mContext.getPackageName(), getAttributionTag())
+ .getOwnerUid();
}
- private void verifyWifiInfoCopyNetCapsForCallerPermission(
- int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+ private void verifyWifiInfoCopyNetCapsPermission(
+ int callerUid, boolean includeLocationSensitiveInfo,
+ boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
final WifiInfo wifiInfo = mock(WifiInfo.class);
when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, callerUid, mContext.getPackageName(), getAttributionTag());
+ netCap, includeLocationSensitiveInfo, callerUid,
+ mContext.getPackageName(), getAttributionTag());
verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
}
+ private void verifyOwnerUidAndWifiInfoNetCapsPermission(
+ boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag,
+ boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag,
+ boolean shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag,
+ boolean shouldInclLocationSensitiveWifiInfoWithIncludeFlag) {
+ final int myUid = Process.myUid();
+
+ final int expectedOwnerUidWithoutIncludeFlag =
+ shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
+ ? Process.myUid() : INVALID_UID;
+ assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
+ myUid, myUid, false /* includeLocationSensitiveInfo */));
+
+ final int expectedOwnerUidWithIncludeFlag =
+ shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID;
+ assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission(
+ myUid, myUid, true /* includeLocationSensitiveInfo */));
+
+ verifyWifiInfoCopyNetCapsPermission(myUid,
+ false, /* includeLocationSensitiveInfo */
+ shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag);
+
+ verifyWifiInfoCopyNetCapsPermission(myUid,
+ true, /* includeLocationSensitiveInfo */
+ shouldInclLocationSensitiveWifiInfoWithIncludeFlag);
+
+ }
+
@Test
- public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ()
+ public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
throws Exception {
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- final int myUid = Process.myUid();
- assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ // Ensure that we include owner uid even if the request asks to remove it since the
+ // app has necessary permissions and targetSdk < S.
+ true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ // Ensure that we remove location info if the request asks to remove it even if the
+ // app has necessary permissions.
+ true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
+ }
+
+ @Test
+ public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag()
+ throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ // Ensure that we include owner uid even if the request asks to remove it since the
+ // app has necessary permissions and targetSdk < S.
+ true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ // Ensure that we remove location info if the request asks to remove it even if the
+ // app has necessary permissions.
+ true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
+ }
+
+ @Test
+ public void
+ testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag()
+ throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
- verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
- true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ // Ensure that we owner UID if the request asks us to remove it even if the app
+ // has necessary permissions since targetSdk >= S.
+ false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ // Ensure that we remove location info if the request asks to remove it even if the
+ // app has necessary permissions.
+ true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
}
@Test
- public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ()
+ public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ()
throws Exception {
setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
- final int myUid = Process.myUid();
- assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
- verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
- true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ // Ensure that we owner UID if the request asks us to remove it even if the app
+ // has necessary permissions since targetSdk >= S.
+ true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ // Ensure that we remove location info if the request asks to remove it even if the
+ // app has necessary permissions.
+ true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
}
@Test
- public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception {
+ public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception {
// Test that even with fine location permission, and UIDs matching, the UID is sanitized.
setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- final int myUid = Process.myUid();
- assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
- verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
- false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
}
@Test
- public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception {
+ public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception {
// Test that even with fine location permission, not being the owner leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final int myUid = Process.myUid();
- assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
-
- verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
- true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+ assertEquals(Process.INVALID_UID,
+ getOwnerUidNetCapsPermission(myUid + 1, myUid,
+ true /* includeLocationSensitiveInfo */));
}
@Test
- public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ()
+ public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ()
throws Exception {
// Test that not having fine location permission leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
- // Test that without the location permission, the owner field is sanitized.
- final int myUid = Process.myUid();
- assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
- verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
- false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
}
@Test
- public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission()
+ public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
throws Exception {
+ // Test that not having fine location permission leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
- // Test that without the location permission, the owner field is sanitized.
- final int myUid = Process.myUid();
- assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
- verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
- false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+ verifyOwnerUidAndWifiInfoNetCapsPermission(
+ false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ );
}
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8972,8 +9201,9 @@ public class ConnectivityServiceTest {
ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
- nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
- INVALID_UID, mQosCallbackTracker);
+ nc, new NetworkScore.Builder().setLegacyInt(0).build(),
+ mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
+ INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies());
}
@Test
@@ -9359,8 +9589,8 @@ public class ConnectivityServiceTest {
assertThrows("Expect throws for invalid request type " + reqTypeInt,
IllegalArgumentException.class,
() -> mService.requestNetwork(nc, reqTypeInt, null, 0, null,
- ConnectivityManager.TYPE_NONE, mContext.getPackageName(),
- getAttributionTag())
+ ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE,
+ mContext.getPackageName(), getAttributionTag())
);
}
}
@@ -9924,9 +10154,12 @@ public class ConnectivityServiceTest {
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
mSystemDefaultNetworkCallback = new TestNetworkCallback();
mDefaultNetworkCallback = new TestNetworkCallback();
+ mProfileDefaultNetworkCallback = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
+ registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
+ TEST_WORK_PROFILE_APP_UID);
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
}
@@ -9938,6 +10171,9 @@ public class ConnectivityServiceTest {
if (null != mSystemDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
}
+ if (null != mProfileDefaultNetworkCallback) {
+ mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
+ }
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -9993,7 +10229,7 @@ public class ConnectivityServiceTest {
oemPrefListener.expectOnComplete();
}
- private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
+ private static class TestOemListenerCallback implements IOnCompleteListener {
final CompletableFuture<Object> mDone = new CompletableFuture<>();
@Override
@@ -10930,4 +11166,496 @@ public class ConnectivityServiceTest {
verifyNoNetwork();
mCm.unregisterNetworkCallback(cellCb);
}
+
+ // Cannot be part of MockNetworkFactory since it requires method of the test.
+ private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) {
+ waitForIdle();
+ factory.assertNoRequestChanged();
+ }
+
+ @Test
+ public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception {
+ // Prepare mock mms factory.
+ final HandlerThread handlerThread = new HandlerThread("MockCellularFactory");
+ handlerThread.start();
+ NetworkCapabilities filter = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_MMS);
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
+ testFactory.setScoreFilter(40);
+
+ try {
+ // Register the factory and expect it will see default request, because all requests
+ // are sent to all factories.
+ testFactory.register();
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
+ // The factory won't try to start the network since the default request doesn't
+ // match the filter (no INTERNET capability).
+ assertFalse(testFactory.getMyStartRequested());
+
+ // Register callback for listening best matching network. Verify that the request won't
+ // be sent to factory.
+ final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+ mCm.registerBestMatchingNetworkCallback(
+ new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+ bestMatchingCb, mCsHandlerThread.getThreadHandler());
+ bestMatchingCb.assertNoCallback();
+ expectNoRequestChanged(testFactory);
+ testFactory.assertRequestCountEquals(1);
+ assertFalse(testFactory.getMyStartRequested());
+
+ // Fire a normal mms request, verify the factory will only see the request.
+ final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest mmsRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_MMS).build();
+ mCm.requestNetwork(mmsRequest, mmsNetworkCallback);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(2);
+ assertTrue(testFactory.getMyStartRequested());
+
+ // Unregister best matching callback, verify factory see no change.
+ mCm.unregisterNetworkCallback(bestMatchingCb);
+ expectNoRequestChanged(testFactory);
+ testFactory.assertRequestCountEquals(2);
+ assertTrue(testFactory.getMyStartRequested());
+ } finally {
+ testFactory.terminate();
+ }
+ }
+
+ @Test
+ public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception {
+ final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+ mCm.registerBestMatchingNetworkCallback(
+ new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(),
+ bestMatchingCb, mCsHandlerThread.getThreadHandler());
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Change something on cellular to trigger capabilities changed, since the callback
+ // only cares about the best network, verify it received nothing from cellular.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ bestMatchingCb.assertNoCallback();
+
+ // Make cellular the best network again, verify the callback now tracks cellular.
+ mWiFiNetworkAgent.adjustScore(-50);
+ bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+ // Make cellular temporary non-trusted, which will not satisfying the request.
+ // Verify the callback switch from/to the other network accordingly.
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+ bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED);
+ bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent);
+
+ // Verify the callback doesn't care about wifi disconnect.
+ mWiFiNetworkAgent.disconnect();
+ bestMatchingCb.assertNoCallback();
+ mCellNetworkAgent.disconnect();
+ bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ }
+
+ private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
+ UidRange range = UidRange.createForUser(handle);
+ return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) };
+ }
+
+ private static class TestOnCompleteListener implements Runnable {
+ final class OnComplete {}
+ final ArrayTrackRecord<OnComplete>.ReadHead mHistory =
+ new ArrayTrackRecord<OnComplete>().newReadHead();
+
+ @Override
+ public void run() {
+ mHistory.add(new OnComplete());
+ }
+
+ public void expectOnComplete() {
+ assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true));
+ }
+ }
+
+ private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception {
+ final NetworkCapabilities workNc = new NetworkCapabilities();
+ workNc.addCapability(NET_CAPABILITY_ENTERPRISE);
+ workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
+ }
+
+ private TestNetworkCallback mEnterpriseCallback;
+ private UserHandle setupEnterpriseNetwork() {
+ final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(userHandle, true);
+
+ // File a request to avoid the enterprise network being disconnected as soon as the default
+ // request goes away – it would make impossible to test that networkRemoveUidRanges
+ // is called, as the network would disconnect first for lack of a request.
+ mEnterpriseCallback = new TestNetworkCallback();
+ final NetworkRequest keepUpRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .build();
+ mCm.requestNetwork(keepUpRequest, mEnterpriseCallback);
+ return userHandle;
+ }
+
+ private void maybeTearDownEnterpriseNetwork() {
+ if (null != mEnterpriseCallback) {
+ mCm.unregisterNetworkCallback(mEnterpriseCallback);
+ }
+ }
+
+ /**
+ * Make sure per-profile networking preference behaves as expected when the enterprise network
+ * goes up and down while the preference is active. Make sure they behave as expected whether
+ * there is a general default network or not.
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDown() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ registerDefaultNetworkCallbacks();
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+
+ // Setting a network preference for this user will create a new set of routing rules for
+ // the UID range that corresponds to this user, so as to define the default network
+ // for these apps separately. This is true because the multi-layer request relevant to
+ // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific
+ // rules to the correct network – in this case the system default network. The case where
+ // the default network for the profile happens to be the same as the system default
+ // is not handled specially, the rules are always active as long as a preference is set.
+ inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ // The enterprise network is not ready yet.
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ mProfileDefaultNetworkCallback);
+
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ workAgent.connect(false);
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
+ mSystemDefaultNetworkCallback.assertNoCallback();
+ mDefaultNetworkCallback.assertNoCallback();
+ inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ // Make sure changes to the work agent send callbacks to the app in the work profile, but
+ // not to the other apps.
+ workAgent.setNetworkValid(true /* isStrictMode */);
+ workAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
+ nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)
+ && nc.hasCapability(NET_CAPABILITY_ENTERPRISE));
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+ workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+ // Conversely, change a capability on the system-wide default network and make sure
+ // that only the apps outside of the work profile receive the callbacks.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ mProfileDefaultNetworkCallback.assertNoCallback();
+
+ // Disconnect and reconnect the system-wide default network and make sure that the
+ // apps on this network see the appropriate callbacks, and the app on the work profile
+ // doesn't because it continues to use the enterprise network.
+ mCellNetworkAgent.disconnect();
+ mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.assertNoCallback();
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.assertNoCallback();
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+
+ // When the agent disconnects, test that the app on the work profile falls back to the
+ // default network.
+ workAgent.disconnect();
+ mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
+
+ mCellNetworkAgent.disconnect();
+ mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+ // If the control comes here, callbacks seem to behave correctly in the presence of
+ // a default network when the enterprise network goes up and down. Now, make sure they
+ // also behave correctly in the absence of a system-wide default network.
+ final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
+ workAgent2.connect(false);
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ workAgent2.setNetworkValid(true /* isStrictMode */);
+ workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
+ nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
+ && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+
+ // When the agent disconnects, test that the app on the work profile falls back to the
+ // default network.
+ workAgent2.disconnect();
+ mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId);
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ mProfileDefaultNetworkCallback);
+
+ // Callbacks will be unregistered by tearDown()
+ }
+
+ /**
+ * Test that, in a given networking context, calling setPreferenceForUser to set per-profile
+ * defaults on then off works as expected.
+ */
+ @Test
+ public void testSetPreferenceForUserOnOff() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ workAgent.connect(true);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ registerDefaultNetworkCallbacks();
+
+ mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ workAgent.disconnect();
+ mCellNetworkAgent.disconnect();
+
+ // Callbacks will be unregistered by tearDown()
+ }
+
+ /**
+ * Test per-profile default networks for two different profiles concurrently.
+ */
+ @Test
+ public void testSetPreferenceForTwoProfiles() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle2 = setupEnterpriseNetwork();
+ final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2);
+ mServiceContext.setWorkProfile(testHandle4, true);
+ registerDefaultNetworkCallbacks();
+
+ final TestNetworkCallback app4Cb = new TestNetworkCallback();
+ final int testWorkProfileAppUid4 =
+ UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID);
+ registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4);
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ workAgent.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle2));
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ app4Cb);
+
+ mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle4));
+
+ app4Cb.expectAvailableCallbacksValidated(workAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ mProfileDefaultNetworkCallback);
+
+ mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle2));
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ app4Cb);
+
+ workAgent.disconnect();
+ mCellNetworkAgent.disconnect();
+
+ mCm.unregisterNetworkCallback(app4Cb);
+ // Other callbacks will be unregistered by tearDown()
+ }
+
+ @Test
+ public void testProfilePreferenceRemovedUponUserRemoved() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER, testHandle);
+ processBroadcast(removedIntent);
+
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+ }
+
+ /**
+ * Make sure that OEM preference and per-profile preference can't be used at the same
+ * time and throw ISE if tried
+ */
+ @Test
+ public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, true);
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+ setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY);
+ assertThrows("Should not be able to set per-profile pref while OEM prefs present",
+ IllegalStateException.class, () ->
+ mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener));
+
+ // Empty the OEM prefs
+ final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
+ final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build();
+ mService.setOemNetworkPreference(emptyOemPref, oemPrefListener);
+ oemPrefListener.expectOnComplete();
+
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ assertThrows("Should not be able to set OEM prefs while per-profile pref is on",
+ IllegalStateException.class , () ->
+ mService.setOemNetworkPreference(emptyOemPref, oemPrefListener));
+ }
+
+ /**
+ * Make sure wrong preferences for per-profile default networking are rejected.
+ */
+ @Test
+ public void testProfileNetworkPrefWrongPreference() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, true);
+ assertThrows("Should not be able to set an illegal preference",
+ IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
+ }
+
+ /**
+ * Make sure requests for per-profile default networking for a non-work profile are
+ * rejected
+ */
+ @Test
+ public void testProfileNetworkPrefWrongProfile() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, false);
+ assertThrows("Should not be able to set a user pref for a non-work profile",
+ IllegalArgumentException.class , () ->
+ mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
+ }
}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index f97eabf6366d..6232423b4f9e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IpSecAlgorithm;
import android.net.IpSecConfig;
@@ -47,6 +48,7 @@ import android.os.Process;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
+import android.util.Range;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -647,9 +649,9 @@ public class IpSecServiceTest {
@Test
public void testReserveNetId() {
- int start = mIpSecService.TUN_INTF_NETID_START;
- for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
- assertEquals(start + i, mIpSecService.reserveNetId());
+ final Range<Integer> netIdRange = ConnectivityManager.getIpSecNetIdRange();
+ for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) {
+ assertEquals(netId, mIpSecService.reserveNetId());
}
// Check that resource exhaustion triggers an exception
@@ -661,7 +663,7 @@ public class IpSecServiceTest {
// Now release one and try again
int releasedNetId =
- mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+ netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2;
mIpSecService.releaseNetId(releasedNetId);
assertEquals(releasedNetId, mIpSecService.reserveNetId());
}
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index a10a3c81bc86..5ec111954fcc 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -21,13 +21,29 @@
package com.android.server
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_WIFI
+import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT
import android.net.ConnectivityManager.TYPE_ETHERNET
import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_MOBILE_CBS
+import android.net.ConnectivityManager.TYPE_MOBILE_DUN
+import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY
+import android.net.ConnectivityManager.TYPE_MOBILE_FOTA
+import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI
+import android.net.ConnectivityManager.TYPE_MOBILE_IA
+import android.net.ConnectivityManager.TYPE_MOBILE_IMS
+import android.net.ConnectivityManager.TYPE_MOBILE_MMS
import android.net.ConnectivityManager.TYPE_MOBILE_SUPL
+import android.net.ConnectivityManager.TYPE_VPN
import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.ConnectivityManager.TYPE_WIFI_P2P
import android.net.ConnectivityManager.TYPE_WIMAX
+import android.net.EthernetManager
import android.net.NetworkInfo.DetailedState.CONNECTED
import android.net.NetworkInfo.DetailedState.DISCONNECTED
+import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.server.ConnectivityService.LegacyTypeTracker
@@ -36,7 +52,6 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
@@ -52,88 +67,130 @@ const val UNSUPPORTED_TYPE = TYPE_WIMAX
@RunWith(AndroidJUnit4::class)
@SmallTest
class LegacyTypeTrackerTest {
- private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
+ private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE,
+ TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI,
+ TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA,
+ TYPE_MOBILE_EMERGENCY, TYPE_VPN)
private val mMockService = mock(ConnectivityService::class.java).apply {
doReturn(false).`when`(this).isDefaultNetwork(any())
}
- private val mTracker = LegacyTypeTracker(mMockService).apply {
- supportedTypes.forEach {
- addSupportedType(it)
- }
+ private val mPm = mock(PackageManager::class.java)
+ private val mContext = mock(Context::class.java).apply {
+ doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI)
+ doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+ doReturn(mPm).`when`(this).packageManager
+ doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService(
+ Context.ETHERNET_SERVICE)
+ }
+ private val mTm = mock(TelephonyManager::class.java).apply {
+ doReturn(true).`when`(this).isDataCapable
+ }
+
+ private fun makeTracker() = LegacyTypeTracker(mMockService).apply {
+ loadSupportedTypes(mContext, mTm)
}
@Test
fun testSupportedTypes() {
- try {
- mTracker.addSupportedType(supportedTypes[0])
- fail("Expected IllegalStateException")
- } catch (expected: IllegalStateException) {}
+ val tracker = makeTracker()
supportedTypes.forEach {
- assertTrue(mTracker.isTypeSupported(it))
+ assertTrue(tracker.isTypeSupported(it))
+ }
+ assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE))
+ }
+
+ @Test
+ fun testSupportedTypes_NoEthernet() {
+ doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE)
+ assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET))
+ }
+
+ @Test
+ fun testSupportedTypes_NoTelephony() {
+ doReturn(false).`when`(mTm).isDataCapable
+ val tracker = makeTracker()
+ val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN)
+ nonMobileTypes.forEach {
+ assertTrue(tracker.isTypeSupported(it))
+ }
+ supportedTypes.toSet().minus(nonMobileTypes).forEach {
+ assertFalse(tracker.isTypeSupported(it))
+ }
+ }
+
+ @Test
+ fun testSupportedTypes_NoWifiDirect() {
+ doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+ val tracker = makeTracker()
+ assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P))
+ supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach {
+ assertTrue(tracker.isTypeSupported(it))
}
- assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE))
}
@Test
fun testSupl() {
+ val tracker = makeTracker()
val mobileNai = mock(NetworkAgentInfo::class.java)
- mTracker.add(TYPE_MOBILE, mobileNai)
+ tracker.add(TYPE_MOBILE, mobileNai)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE)
reset(mMockService)
- mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+ tracker.add(TYPE_MOBILE_SUPL, mobileNai)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
reset(mMockService)
- mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
+ tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
reset(mMockService)
- mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+ tracker.add(TYPE_MOBILE_SUPL, mobileNai)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
reset(mMockService)
- mTracker.remove(mobileNai, false)
+ tracker.remove(mobileNai, false)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE)
}
@Test
fun testAddNetwork() {
+ val tracker = makeTracker()
val mobileNai = mock(NetworkAgentInfo::class.java)
val wifiNai = mock(NetworkAgentInfo::class.java)
- mTracker.add(TYPE_MOBILE, mobileNai)
- mTracker.add(TYPE_WIFI, wifiNai)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.add(TYPE_MOBILE, mobileNai)
+ tracker.add(TYPE_WIFI, wifiNai)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Make sure adding a second NAI does not change the results.
val secondMobileNai = mock(NetworkAgentInfo::class.java)
- mTracker.add(TYPE_MOBILE, secondMobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.add(TYPE_MOBILE, secondMobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Make sure removing a network that wasn't added for this type is a no-op.
- mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Remove the top network for mobile and make sure the second one becomes the network
// of record for this type.
- mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Make sure adding a network for an unsupported type does not register it.
- mTracker.add(UNSUPPORTED_TYPE, mobileNai)
- assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE))
+ tracker.add(UNSUPPORTED_TYPE, mobileNai)
+ assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE))
}
@Test
fun testBroadcastOnDisconnect() {
+ val tracker = makeTracker()
val mobileNai1 = mock(NetworkAgentInfo::class.java)
val mobileNai2 = mock(NetworkAgentInfo::class.java)
doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
- mTracker.add(TYPE_MOBILE, mobileNai1)
+ tracker.add(TYPE_MOBILE, mobileNai1)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
reset(mMockService)
doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
- mTracker.add(TYPE_MOBILE, mobileNai2)
+ tracker.add(TYPE_MOBILE, mobileNai2)
verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
- mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
+ tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE)
}
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 5760211d9a27..b7ece8f4c4c9 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -312,14 +312,14 @@ public class DnsManagerTest {
@Test
public void testOverrideDefaultMode() throws Exception {
// Hard-coded default is opportunistic mode.
- final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mContentResolver);
+ final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx);
assertTrue(cfgAuto.useTls);
assertEquals("", cfgAuto.hostname);
assertEquals(new InetAddress[0], cfgAuto.ips);
// Pretend a gservices push sets the default to "off".
Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "off");
- final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mContentResolver);
+ final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx);
assertFalse(cfgOff.useTls);
assertEquals("", cfgOff.hostname);
assertEquals(new InetAddress[0], cfgOff.ips);
@@ -328,7 +328,7 @@ public class DnsManagerTest {
Settings.Global.putString(
mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
- final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mContentResolver);
+ final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx);
assertTrue(cfgStrict.useTls);
assertEquals("strictmode.com", cfgStrict.hostname);
assertEquals(new InetAddress[0], cfgStrict.ips);
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index a913673c2a1e..ea2b362c537a 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -40,6 +40,7 @@ import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkProvider;
+import android.net.NetworkScore;
import android.os.Binder;
import android.text.format.DateUtils;
@@ -355,9 +356,10 @@ public class LingerMonitorTest {
caps.addCapability(0);
caps.addTransportType(transport);
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
- new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
- mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
- mQosCallbackTracker);
+ new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
+ mCtx, null, new NetworkAgentConfig() /* config */, mConnService, mNetd,
+ mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
+ mQosCallbackTracker, new ConnectivityService.Dependencies());
nai.everValidated = true;
return nai;
}
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 5f56e25356c2..9b2a638f8b39 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -16,11 +16,15 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -34,6 +38,7 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.test.TestLooper;
@@ -72,11 +77,15 @@ public class Nat464XlatTest {
Handler mHandler;
NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
- Nat464Xlat makeNat464Xlat() {
- return new Nat464Xlat(mNai, mNetd, mDnsResolver) {
+ Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
+ return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) {
@Override protected int getNetId() {
return NETID;
}
+
+ @Override protected boolean isCellular464XlatEnabled() {
+ return isCellular464XlatEnabled;
+ }
};
}
@@ -99,6 +108,7 @@ public class Nat464XlatTest {
mNai.linkProperties.setInterfaceName(BASE_IFACE);
mNai.networkInfo = new NetworkInfo(null);
mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+ mNai.networkCapabilities = new NetworkCapabilities();
markNetworkConnected();
when(mNai.connService()).thenReturn(mConnectivity);
when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
@@ -110,21 +120,23 @@ public class Nat464XlatTest {
}
private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
+ Nat464Xlat nat = makeNat464Xlat(true);
String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
- assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
+ assertEquals(msg, expected, nat.requiresClat(nai));
}
private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
+ Nat464Xlat nat = makeNat464Xlat(true);
String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
- assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
+ assertEquals(msg, expected, nat.shouldStartClat(nai));
}
@Test
@@ -194,7 +206,7 @@ public class Nat464XlatTest {
}
private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
- Nat464Xlat nat = makeNat464Xlat();
+ Nat464Xlat nat = makeNat464Xlat(true);
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
mNai.linkProperties.addLinkAddress(V6ADDR);
@@ -245,7 +257,7 @@ public class Nat464XlatTest {
}
private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
- Nat464Xlat nat = makeNat464Xlat();
+ Nat464Xlat nat = makeNat464Xlat(true);
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
InOrder inOrder = inOrder(mNetd, mConnectivity);
@@ -335,7 +347,7 @@ public class Nat464XlatTest {
@Test
public void testClatdCrashWhileRunning() throws Exception {
- Nat464Xlat nat = makeNat464Xlat();
+ Nat464Xlat nat = makeNat464Xlat(true);
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
@@ -372,7 +384,7 @@ public class Nat464XlatTest {
}
private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
- Nat464Xlat nat = makeNat464Xlat();
+ Nat464Xlat nat = makeNat464Xlat(true);
mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
@@ -414,7 +426,7 @@ public class Nat464XlatTest {
}
private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
- Nat464Xlat nat = makeNat464Xlat();
+ Nat464Xlat nat = makeNat464Xlat(true);
mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
@@ -450,7 +462,7 @@ public class Nat464XlatTest {
final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
- Nat464Xlat nat = makeNat464Xlat();
+ Nat464Xlat nat = makeNat464Xlat(true);
final LinkProperties emptyLp = new LinkProperties();
LinkProperties fixedupLp;
@@ -486,10 +498,57 @@ public class Nat464XlatTest {
assertEquals(null, fixedupLp.getNat64Prefix());
}
+ private void checkClatDisabledOnCellular(boolean onCellular) throws Exception {
+ // Disable 464xlat on cellular networks.
+ Nat464Xlat nat = makeNat464Xlat(false);
+ mNai.linkProperties.addLinkAddress(V6ADDR);
+ mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular);
+ nat.update();
+
+ final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX);
+ if (onCellular) {
+ // Prefix discovery is never started.
+ verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID));
+ assertIdle(nat);
+
+ // If a NAT64 prefix comes in from an RA, clat is not started either.
+ mNai.linkProperties.setNat64Prefix(nat64Prefix);
+ nat.setNat64PrefixFromRa(nat64Prefix);
+ nat.update();
+ verify(mNetd, never()).clatdStart(anyString(), anyString());
+ assertIdle(nat);
+ } else {
+ // Prefix discovery is started.
+ verify(mDnsResolver).startPrefix64Discovery(eq(NETID));
+ assertIdle(nat);
+
+ // If a NAT64 prefix comes in from an RA, clat is started.
+ mNai.linkProperties.setNat64Prefix(nat64Prefix);
+ nat.setNat64PrefixFromRa(nat64Prefix);
+ nat.update();
+ verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX);
+ assertStarting(nat);
+ }
+ }
+
+ @Test
+ public void testClatDisabledOnCellular() throws Exception {
+ checkClatDisabledOnCellular(true);
+ }
+
+ @Test
+ public void testClatDisabledOnNonCellular() throws Exception {
+ checkClatDisabledOnCellular(false);
+ }
+
static void assertIdle(Nat464Xlat nat) {
assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
}
+ static void assertStarting(Nat464Xlat nat) {
+ assertTrue("Nat464Xlat was not STARTING", nat.isStarting());
+ }
+
static void assertRunning(Nat464Xlat nat) {
assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index e4e24b464838..fec5ef39374a 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -48,18 +48,22 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.net.INetd;
import android.net.UidRange;
+import android.net.Uri;
import android.os.Build;
import android.os.SystemConfigManager;
import android.os.UserHandle;
@@ -70,12 +74,11 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.LocalServices;
-import com.android.server.pm.PackageList;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -112,7 +115,6 @@ public class PermissionMonitorTest {
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
@Mock private INetd mNetdService;
- @Mock private PackageManagerInternal mMockPmi;
@Mock private UserManager mUserManager;
@Mock private PermissionMonitor.Dependencies mDeps;
@Mock private SystemConfigManager mSystemConfigManager;
@@ -131,16 +133,14 @@ public class PermissionMonitorTest {
when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
.thenReturn(mSystemConfigManager);
when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+ final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+ doReturn(UserHandle.ALL).when(asUserCtx).getUser();
+ when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mMockPmi);
- when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(),
- /* observer */ null));
when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
mPermissionMonitor.startMonitoring();
- verify(mMockPmi).getPackageList(mPermissionMonitor);
}
private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
@@ -770,4 +770,32 @@ public class PermissionMonitorTest {
INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
new int[]{ MOCK_UID2 });
}
+
+ @Test
+ public void testIntentReceiver() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
+ final BroadcastReceiver receiver = receiverCaptor.getValue();
+
+ // Verify receiving PACKAGE_ADDED intent.
+ final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+ Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+ addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+ setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1,
+ new String[] { INTERNET, UPDATE_DEVICE_STATS });
+ receiver.onReceive(mContext, addedIntent);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 });
+
+ // Verify receiving PACKAGE_REMOVED intent.
+ when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null);
+ final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+ Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+ removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+ receiver.onReceive(mContext, removedIntent);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 });
+ }
+
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index b8f7fbca3983..11fcea60d98d 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1026,7 +1026,11 @@ public class VpnTest {
.thenReturn(new Network[] { new Network(101) });
when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
- anyInt(), any(), anyInt())).thenReturn(new Network(102));
+ any(), any(), anyInt())).thenAnswer(invocation -> {
+ // The runner has registered an agent and is now ready.
+ legacyRunnerReady.open();
+ return new Network(102);
+ });
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
@@ -1048,7 +1052,7 @@ public class VpnTest {
ArgumentCaptor<NetworkCapabilities> ncCaptor =
ArgumentCaptor.forClass(NetworkCapabilities.class);
verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
- lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt());
+ lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt());
// In this test the expected address is always v4 so /32.
// Note that the interface needs to be specified because RouteInfo objects stored in
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a07bce34c737..8932892c3aec 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -30,12 +30,21 @@ import com.android.test.filters.SelectTest;
* -e selectTest_verbose true \
* com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
* </pre>
+ *
+ * <p>Use this filter when running FrameworksMockingCoreTests as
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.server.wm.test.filters.FrameworksTestsFilter \
+ * -e selectTest_verbose true \
+ * com.android.frameworks.mockingcoretests/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
*/
public final class FrameworksTestsFilter extends SelectTest {
private static final String[] SELECTED_TESTS = {
// Test specifications for FrameworksMockingCoreTests.
"android.app.activity.ActivityThreadClientTest",
+ "android.view.DisplayTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
"android.view.CutoutSpecificationTest",
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 11498dec8165..814cad4ab448 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -74,7 +74,7 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;
@@ -593,6 +593,16 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
+ @Test(expected = SecurityException.class)
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+ doThrow(new SecurityException())
+ .when(mMockContext)
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
+
@Test
public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 1d459a347526..1ef1a61f17ea 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -194,29 +194,35 @@ public class UnderlyingNetworkTrackerTest {
}
private NetworkRequest getWifiRequest() {
- return getExpectedRequestBase()
+ return getExpectedRequestBase(true)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
}
private NetworkRequest getCellRequestForSubId(int subId) {
- return getExpectedRequestBase()
+ return getExpectedRequestBase(false)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
}
private NetworkRequest getRouteSelectionRequest() {
- return getExpectedRequestBase().build();
+ return getExpectedRequestBase(true).build();
}
- private NetworkRequest.Builder getExpectedRequestBase() {
- return new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ private NetworkRequest.Builder getExpectedRequestBase(boolean requireVcnManaged) {
+ final NetworkRequest.Builder builder =
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ if (requireVcnManaged) {
+ builder.addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ return builder;
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 4bf849111a5d..ca6448ca9b8c 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -73,7 +73,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
- mIkeSession = mGatewayConnection.buildIkeSession();
+ mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
mGatewayConnection.setIkeSession(mIkeSession);
mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
@@ -188,7 +188,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
any(),
lpCaptor.capture(),
ncCaptor.capture(),
- anyInt(),
+ any(),
any(),
anyInt());
verify(mIpSecSvc)
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index d07d2cf4f1bb..7afa4494ee8b 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -25,12 +25,15 @@ import static org.mockito.Matchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.net.ipsec.ike.IkeSessionParams;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
/** Tests for VcnGatewayConnection.ConnectingState */
@RunWith(AndroidJUnit4.class)
@@ -51,7 +54,12 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
@Test
public void testEnterStateCreatesNewIkeSession() throws Exception {
- verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+ final ArgumentCaptor<IkeSessionParams> paramsCaptor =
+ ArgumentCaptor.forClass(IkeSessionParams.class);
+ verify(mDeps).newIkeSession(any(), paramsCaptor.capture(), any(), any(), any());
+ assertEquals(
+ TEST_UNDERLYING_NETWORK_RECORD_1.network,
+ paramsCaptor.getValue().getConfiguredNetwork());
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 661e03af4f84..99feffdebc8e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -38,7 +38,8 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
public void setUp() throws Exception {
super.setUp();
- mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+ mGatewayConnection.setIkeSession(
+ mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_2.network));
// ensure that mGatewayConnection has an underlying Network before entering
// DisconnectingState
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 748c7924685d..d08af9dd3370 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -18,6 +18,7 @@ package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -87,6 +88,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
underlyingCaps.addTransportType(transportType);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);